diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..25b81e202d1bbfd67b73640596c84d350e6dc9ae --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +**/compile_commands.json +build +build_* +.vscode +.idea +*cmake-build-* +cts-generated +.dir-locals.el +artifacts/ +cscope* +tags +.byebug_history +.cache +*.swp diff --git a/BUILD.gn b/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..86e03b4b58b5bc442bf47422f1fc5070e2c9c4df --- /dev/null +++ b/BUILD.gn @@ -0,0 +1,353 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//build/ohos.gni") + +group("ark_js_packages") { + deps = [] + if (host_os != "mac") { + deps += [ + "//ark/js_runtime:libark_jsruntime", + "//ark/js_runtime/ecmascript/js_vm:ark_js_vm", + "//ark/js_runtime/ecmascript/tooling:libark_ecma_debugger", + ] + } +} + +group("ark_js_host_linux_tools_packages") { + deps = [] + if (host_os != "mac") { + deps += [ + "//ark/js_runtime:libark_jsruntime(${host_toolchain})", + "//ark/js_runtime/ecmascript/js_vm:ark_js_vm(${host_toolchain})", + + #"//ark/js_runtime/ecmascript/compiler:libark_jsoptimizer(${host_toolchain})", + ] + if (current_cpu == "x86_64" || current_cpu == "x64") { + deps += [ "//ark/js_runtime/ecmascript/compiler:libark_jsoptimizer(${host_toolchain})" ] + } + } +} + +group("ark_js_unittest") { + testonly = true + deps = [] + if (host_os != "mac") { + deps += [ + "//ark/js_runtime/ecmascript/builtins/tests:unittest", + "//ark/js_runtime/ecmascript/hprof/tests:unittest", + "//ark/js_runtime/ecmascript/napi/test:unittest", + "//ark/js_runtime/ecmascript/regexp/tests:unittest", + "//ark/js_runtime/ecmascript/snapshot/tests:unittest", + "//ark/js_runtime/ecmascript/tests:unittest", + "//ark/js_runtime/ecmascript/tooling/test:unittest", + ] + } +} + +group("ark_js_host_unittest") { + testonly = true + deps = [] + if (host_os != "mac") { + # js unittest + deps += [ + "//ark/js_runtime/ecmascript/builtins/tests:host_unittest", + "//ark/js_runtime/ecmascript/hprof/tests:host_unittest", + "//ark/js_runtime/ecmascript/napi/test:host_unittest", + "//ark/js_runtime/ecmascript/regexp/tests:host_unittest", + "//ark/js_runtime/ecmascript/snapshot/tests:host_unittest", + "//ark/js_runtime/ecmascript/tests:host_unittest", + "//ark/js_runtime/ecmascript/tooling/test:host_unittest", + ] + if (current_cpu == "x86_64" || current_cpu == "x64") { + deps += [ "//ark/js_runtime/ecmascript/compiler:libark_jsoptimizer(${host_toolchain})" ] + } + + # js bytecode test + deps += [ "//ark/js_runtime/test/moduletest:ark_js_moduletest" ] + } +} + +config("ark_jsruntime_public_config") { + configs = [ + "$ark_root/libpandabase:arkbase_public_config", + "$ark_root/libpandafile:arkfile_public_config", + sdk_libc_secshared_config, + ] + + include_dirs = [ + "//ark/js_runtime", + "$ark_root", + ] +} + +config("ark_jsruntime_config") { + defines = [ "PANDA_ENABLE_LTO" ] + + if (is_linux) { + defines += [ + "PANDA_TARGET_UNIX", + "PANDA_TARGET_LINUX", + "PANDA_USE_FUTEX", + ] + } else if (is_mingw) { + defines += [ + "PANDA_TARGET_WINDOWS", + "_CRTBLD", + "__LIBMSVCRT__", + ] + } else if (is_mac) { + defines += [ + "PANDA_TARGET_UNIX", + "PANDA_TARGET_MACOS", + "PANDA_USE_FUTEX", + ] + } else { + defines += [ + "PANDA_TARGET_UNIX", + "PANDA_USE_FUTEX", + ] + } + + cflags_cc = [ + "-pedantic", + "-Wno-invalid-offsetof", + "-Wno-gnu-statement-expression", + "-pipe", + "-Wdate-time", + "-Wformat=2", + + #"-Wshadow", + ] + + if (is_debug) { + cflags_cc += [ + "-Og", + "-O0", + "-ggdb3", + ] + } else { + defines += [ "NDEBUG" ] + } + + if (current_cpu == "arm") { + defines += [ + "PANDA_TARGET_ARM32_ABI_SOFT=1", + "PANDA_TARGET_ARM32", + ] + } else if (current_cpu == "arm64") { + defines += [ + "PANDA_TARGET_ARM64", + "PANDA_TARGET_64", + "PANDA_ENABLE_GLOBAL_REGISTER_VARIABLES", + "PANDA_USE_32_BIT_POINTER", + ] + } else if (current_cpu == "x86") { + defines += [ "PANDA_TARGET_X86" ] + } else if (current_cpu == "amd64" || current_cpu == "x64" || + current_cpu == "x86_64") { + defines += [ + "PANDA_TARGET_64", + "PANDA_TARGET_AMD64", + "PANDA_USE_32_BIT_POINTER", + ] + } +} + +# ecmascript unit testcase config +config("ecma_test_config") { + visibility = [ ":*" ] + + cflags_cc = [ "-Wno-sign-compare" ] +} + +ecma_source = [ + "ecmascript/base/array_helper.cpp", + "ecmascript/base/builtins_base.cpp", + "ecmascript/base/error_helper.cpp", + "ecmascript/base/json_parser.cpp", + "ecmascript/base/json_stringifier.cpp", + "ecmascript/base/number_helper.cpp", + "ecmascript/base/string_helper.cpp", + "ecmascript/base/typed_array_helper.cpp", + "ecmascript/base/utf_helper.cpp", + "ecmascript/builtins.cpp", + "ecmascript/builtins/builtins_array.cpp", + "ecmascript/builtins/builtins_arraybuffer.cpp", + "ecmascript/builtins/builtins_async_function.cpp", + "ecmascript/builtins/builtins_boolean.cpp", + "ecmascript/builtins/builtins_dataview.cpp", + "ecmascript/builtins/builtins_date.cpp", + "ecmascript/builtins/builtins_errors.cpp", + "ecmascript/builtins/builtins_function.cpp", + "ecmascript/builtins/builtins_generator.cpp", + "ecmascript/builtins/builtins_global.cpp", + "ecmascript/builtins/builtins_iterator.cpp", + "ecmascript/builtins/builtins_json.cpp", + "ecmascript/builtins/builtins_map.cpp", + "ecmascript/builtins/builtins_math.cpp", + "ecmascript/builtins/builtins_number.cpp", + "ecmascript/builtins/builtins_object.cpp", + "ecmascript/builtins/builtins_promise.cpp", + "ecmascript/builtins/builtins_promise_handler.cpp", + "ecmascript/builtins/builtins_promise_job.cpp", + "ecmascript/builtins/builtins_proxy.cpp", + "ecmascript/builtins/builtins_reflect.cpp", + "ecmascript/builtins/builtins_regexp.cpp", + "ecmascript/builtins/builtins_set.cpp", + "ecmascript/builtins/builtins_string.cpp", + "ecmascript/builtins/builtins_string_iterator.cpp", + "ecmascript/builtins/builtins_symbol.cpp", + "ecmascript/builtins/builtins_typedarray.cpp", + "ecmascript/builtins/builtins_weak_map.cpp", + "ecmascript/builtins/builtins_weak_set.cpp", + "ecmascript/class_linker/panda_file_translator.cpp", + "ecmascript/dump.cpp", + "ecmascript/ecma_class_linker_extension.cpp", + "ecmascript/ecma_exceptions.cpp", + "ecmascript/ecma_language_context.cpp", + "ecmascript/ecma_module.cpp", + "ecmascript/ecma_string.cpp", + "ecmascript/ecma_string_table.cpp", + "ecmascript/ecma_vm.cpp", + "ecmascript/free_object.cpp", + "ecmascript/generator_helper.cpp", + "ecmascript/global_env.cpp", + "ecmascript/global_env_constants.cpp", + "ecmascript/hprof/heap_profiler.cpp", + "ecmascript/hprof/heap_profiler_interface.cpp", + "ecmascript/hprof/heap_root_visitor.cpp", + "ecmascript/hprof/heap_snapshot.cpp", + "ecmascript/hprof/heap_snapshot_json_serializer.cpp", + "ecmascript/hprof/heap_tracker.cpp", + "ecmascript/hprof/string_hashmap.cpp", + "ecmascript/ic/profile_type_info.cpp", + "ecmascript/ic/ic_runtime.cpp", + "ecmascript/ic/property_box.cpp", + "ecmascript/ic/proto_change_details.cpp", + "ecmascript/interpreter/frame_handler.cpp", + "ecmascript/interpreter/slow_runtime_helper.cpp", + "ecmascript/interpreter/slow_runtime_stub.cpp", + "ecmascript/jobs/micro_job_queue.cpp", + "ecmascript/js_arguments.cpp", + "ecmascript/js_array.cpp", + "ecmascript/js_array_iterator.cpp", + "ecmascript/js_arraybuffer.cpp", + "ecmascript/js_async_function.cpp", + "ecmascript/js_dataview.cpp", + "ecmascript/js_date.cpp", + "ecmascript/js_for_in_iterator.cpp", + "ecmascript/js_function.cpp", + "ecmascript/js_generator_object.cpp", + "ecmascript/js_hclass.cpp", + "ecmascript/js_invoker.cpp", + "ecmascript/js_iterator.cpp", + "ecmascript/js_map.cpp", + "ecmascript/js_map_iterator.cpp", + "ecmascript/js_object.cpp", + "ecmascript/js_primitive_ref.cpp", + "ecmascript/js_promise.cpp", + "ecmascript/js_proxy.cpp", + "ecmascript/js_serializer.cpp", + "ecmascript/js_set.cpp", + "ecmascript/js_set_iterator.cpp", + "ecmascript/js_stable_array.cpp", + "ecmascript/js_string_iterator.cpp", + "ecmascript/js_tagged_value.cpp", + "ecmascript/js_thread.cpp", + "ecmascript/js_typed_array.cpp", + "ecmascript/js_weak_container.cpp", + "ecmascript/linked_hash_table.cpp", + "ecmascript/literal_data_extractor.cpp", + "ecmascript/mem/c_string.cpp", + "ecmascript/mem/chunk.cpp", + "ecmascript/mem/compress_collector.cpp", + "ecmascript/mem/ecma_heap_manager.cpp", + "ecmascript/mem/free_object_kind.cpp", + "ecmascript/mem/free_object_list.cpp", + "ecmascript/mem/gc_stats.cpp", + "ecmascript/mem/heap.cpp", + "ecmascript/mem/mem_controller.cpp", + "ecmascript/mem/old_space_collector.cpp", + "ecmascript/mem/region_factory.cpp", + "ecmascript/mem/semi_space_collector.cpp", + "ecmascript/mem/semi_space_marker.cpp", + "ecmascript/mem/semi_space_worker.cpp", + "ecmascript/mem/space.cpp", + "ecmascript/mem/tagged_object.cpp", + "ecmascript/mem/verification.cpp", + "ecmascript/napi/jsnapi.cpp", + "ecmascript/object_factory.cpp", + "ecmascript/object_operator.cpp", + "ecmascript/layout_info.cpp", + "ecmascript/regexp/dyn_chunk.cpp", + "ecmascript/regexp/regexp_executor.cpp", + "ecmascript/regexp/regexp_opcode.cpp", + "ecmascript/regexp/regexp_parser.cpp", + "ecmascript/regexp/regexp_parser_cache.cpp", + "ecmascript/snapshot/mem/slot_bit.cpp", + "ecmascript/snapshot/mem/snapshot.cpp", + "ecmascript/snapshot/mem/snapshot_serialize.cpp", + "ecmascript/tagged_dictionary.cpp", + "ecmascript/template_string.cpp", + "ecmascript/vmstat/caller_stat.cpp", + "ecmascript/vmstat/runtime_stat.cpp", + "ecmascript/weak_vector.cpp", +] + +ohos_static_library("libark_jsruntime_static") { + sources = ecma_source + sources += [ + "ecmascript/tooling/interface/debugger_api.cpp", + "ecmascript/tooling/interface/js_debugger.cpp", + ] + + configs = [ + ":ark_jsruntime_public_config", # should add before + # arkruntime_public_config + ":ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + "$ark_root/libpandafile:libarkfile", + "//third_party/icu/icu4c:shared_icui18n", + "//third_party/icu/icu4c:shared_icuuc", + sdk_libc_secshared_dep, + ] + + if (is_standard_system) { + cflags_cc = [ "-fvisibility=hidden" ] + deps += [ "$ark_root/runtime:libarkruntime_static" ] + } else { + deps += [ "$ark_root/runtime:libarkruntime" ] + } + + output_extension = "a" + subsystem_name = "ark" +} + +ohos_shared_library("libark_jsruntime") { + deps = [ ":libark_jsruntime_static" ] + + install_enable = true + part_name = "ark_js_runtime" + + output_extension = "so" + if (!is_standard_system) { + relative_install_dir = "ark" + } + subsystem_name = "ark" +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..4a459866a57c25462afad17f3fe0b50d440da080 --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/OAT.xml b/OAT.xml new file mode 100644 index 0000000000000000000000000000000000000000..b1ce5cd042349171716a0fdbdacc43ef85be292a --- /dev/null +++ b/OAT.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 27e727f46828479f15fb838c643c0f08908ccd98..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# ark_js_runtime - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index fcec4f7d2cf2e31b7b6c754d578eaa5d9375e1f3..a04c8b6b1b47e3349f946bc14e893f9cfd502e00 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,77 @@ -# ark_js_runtime +# ARK JS Runtime Module -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} +- [Directory Structure](#section161941989596) +- [Constraints](#section119744591305) +- [Build](#section137768191623) + - [Available APIs](#section175841548124517) + - [Usage Guidelines](#section129654513264) -#### 软件架构 -软件架构说明 +- [Repositories Involved](#section1371113476307) +### Introduction -#### 安装教程 +ARK JS Runtime is the runtime used by JavaScript \(JS\) applications on OpenHarmony. It consists of the JS object allocator, garbage collector \(GC\), a standard library compliant with ECMAScript specifications, ARK bytecode interpreter, inline caches that store hidden classes, and the ARK Foreign Function Interface \(AFFI\). -1. xxxx -2. xxxx -3. xxxx +**ARK JS runtime architecture** -#### 使用说明 +![](docs/figures/en-us_image_0000001149439242.png) -1. xxxx -2. xxxx -3. xxxx +## Directory Structure -#### 参与贡献 +``` +/ark/js_runtime +├─ ecmascript # JS object definition +│ ├─ base # Base helper class +│ ├─ builtins # ECMAScript library +│ ├─ class_linker # Bytecode pre-processing module +│ ├─ compiler # JS compiler +│ ├─ hprof # Memory analysis utility class +│ ├─ ic # Inline cache module +│ ├─ interpreter # JS interpreter +│ ├─ jobs # Queue of jobs +│ ├─ js_vm # ARK command line tool +│ ├─ mem # Memory management module +│ ├─ napi # External native interface +│ ├─ regexp # Regular expression engine module +│ ├─ snapshot/mem # Snapshot module +│ ├─ tests # Unit test cases +│ ├─ thread # Thread pool +│ ├─ tooling # JS debugger +│ └─ vmstat # Runtime status utility classes +└─ test # Module test cases +``` -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +## Constraints +* Only the bytecode file generated by the ARK JS frontend can be run. +* Only support ES2015 standard and strict mode ("use strict"). +* Don't support dynamically create functions via string (e.g., new Function("console.log(1))). -#### 特技 +## Build + +./build.sh --product-name Hi3516DV300 --build-target ark\_js\_runtime + +### Available APIs + +For details, see [NAPI](https://gitee.com/openharmony/ace_napi/blob/master/README.md). + +### Usage Guidelines + +For details about how to generate JS bytecodes, see [Using the Toolchain](docs/using-the-toolchain.md). + +To run bytecodes: + +cd out/release + +LD\_LIBRARY\_PATH=clang\_x64/ark/ark:clang\_x64/global/i18n:../../prebuilts/clang/ohos/linux-x86\_64/llvm/lib/ ./clang\_x64/ark/ark/ark\_js\_vm helloworld.abc \_GLOBAL::func\_main\_0 + +## Repositories Involved + +[ARK Runtime Subsystem](docs/ARK-Runtime-Subsystem.md) + +[ark/runtime_core](https://gitee.com/openharmony/ark_runtime_core/blob/master/README.md) + +**[ark/js\_runtime](README.md)** + +[ark/ts2abc](https://gitee.com/openharmony/ark_ts2abc/blob/master/README.md) -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README_zh.md b/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..65616edc35b8aa9d416dba5c3149e2da115919d2 --- /dev/null +++ b/README_zh.md @@ -0,0 +1,77 @@ +# 方舟JS运行时组件 + +- [目录](#section161941989596) +- [约束](#section119744591305) +- [编译构建](#section137768191623) + - [接口说明](#section175841548124517) + - [使用说明](#section129654513264) + +- [相关仓](#section1371113476307) + +### 简介 + +**方舟JS运行时(ARK JavaScript Runtime)**是OpenHarmony上JS应用使用的运行时。包含JS对象的分配器以及垃圾回收器(GC)、符合ECMAScript规范的标准库、用于运行ARK前端组件生成的方舟字节码(ARK Bytecode,abc)的解释器、用于存储隐藏类的内联缓存、方舟JS运行时对外的函数接口(AFFI)等模块。 + +**方舟JS运行时组件架构图:** + +![](docs/figures/zh-cn_image_0000001196712959.png) + +## 目录 + +``` +/ark/js_runtime +├─ ecmascript # 方舟JS运行时实现,包括ECMAScript标准库、解释器、内存管理等 +│ ├─ base # 基础帮助类 +│ ├─ builtins # ECMAScript标准库 +│ ├─ class_linker # 字节码预处理模块 +│ ├─ compiler # JS编译器 +│ ├─ hprof # 内存分析工具 +│ ├─ ic # 内联缓存模块 +│ ├─ interpreter # JS解释器 +│ ├─ jobs # 微任务队列 +│ ├─ js_vm # 命令行工具 +│ ├─ mem # 内存管理模块 +│ ├─ napi # C++接口模块 +│ ├─ regexp # 正则引擎模块 +│ ├─ snapshot # 快照模块 +│ ├─ tests # 单元测试用例 +│ ├─ thread # 线程池 +│ ├─ tooling # JS调试器 +│ └─ vmstat # 运行时profiling工具 +└─ test # 模块测试用例 +``` + +## 约束 + +* 仅支持运行方舟JS前端工具链\(ts2abc\)生成的方舟字节码文件 +* 只支持ES2015标准和严格模式(use strict) +* 不支持通过字符串动态创建函数(比如new Function("console.log(1);")) + +## 编译构建 + +./build.sh --product-name Hi3516DV300 --build-target ark\_js\_runtime + +### 接口说明 + +NAPI接口说明参考[NAPI组件](https://gitee.com/openharmony/ace_napi/blob/master/README_zh.md) + +### 使用说明 + +JS生成字节码参考[工具链使用](docs/工具链使用.md) + +字节码执行 + +cd out/release + +LD\_LIBRARY\_PATH=clang\_x64/ark/ark:clang\_x64/global/i18n:../../prebuilts/clang/ohos/linux-x86\_64/llvm/lib/ ./clang\_x64/ark/ark\_js\_runtime/ark\_js\_vm helloworld.abc \_GLOBAL::func\_main\_0 + +## 相关仓 + +[方舟运行时子系统](docs/方舟运行时子系统.md) + +[ark/runtime\_core](https://gitee.com/openharmony/ark_runtime_core/blob/master/README_zh.md) + +**[ark/js\_runtime](README_zh.md)** + +[ark/ts2abc](https://gitee.com/openharmony/ark_ts2abc/blob/master/README_zh.md) + diff --git a/docs/ARK Runtime Usage Guide.md b/docs/ARK Runtime Usage Guide.md new file mode 100644 index 0000000000000000000000000000000000000000..4ee1d8459181ab7742070f847750bdbb413d0ded --- /dev/null +++ b/docs/ARK Runtime Usage Guide.md @@ -0,0 +1,7 @@ +# ARK Runtime Usage Guide + +- [Overview](overview.md) +- [Environment Setup and Compilation](environment-setup-and-compilation.md) +- [Development Example](development-example.md) +- [Using the Toolchain](using-the-toolchain.md) + diff --git a/docs/ARK-Runtime-Subsystem.md b/docs/ARK-Runtime-Subsystem.md new file mode 100644 index 0000000000000000000000000000000000000000..58a909c181d610245a8e769356baff8eeeadabad --- /dev/null +++ b/docs/ARK-Runtime-Subsystem.md @@ -0,0 +1,67 @@ +# ARK Runtime Subsystem + +- [Introduction](#section11660541593) +- [Directory Structure](#section161941989596) +- [Note](#section18393638195820) +- [Repositories Involved](#section1371113476307) + +## Introduction + +ARK is a unified programming platform developed by Huawei. Its key components include a compiler, toolchain, and runtime. ARK supports compilation and running of high-level programming languages on the multi-chip platform and accelerates the running of the OpenHarmony operating system and its applications and services on mobile phones, PCs, tablets, TVs, automobiles, and smart wearables. The ARK-JS open sourced this time provides the capability of compiling and running the JavaScript \(JS\) language on the OpenHarmony operating system. + +The ARK-JS consists of two parts: JS compiler toolchain and JS runtime. The JS compiler toolchain compiles JS source code into ARK bytecodes. The JS runtime executes the generated ARK bytecodes. Unless otherwise specified, bytecodes refer to ARK bytecodes in this document. + +The following figure shows the architecture of the JS compiler toolchain. + +![](figures/en-us_image_0000001197967983.png) + +The JS front-end compiler parses the JS source code into an abstract syntax tree \(AST\), which is processed by the AST transformer, bytecode generator, and register allocator. The native emiter generates the ARK bytecode file \(.abc\). + +The following figure shows the JS runtime architecture. + +![](figures/en-us_image_0000001197275269.png) + +ARK-JS Runtime runs ARK bytecode files to implement JS semantic logic. + +ARK-JS Runtime consists of the following: + +- Core Runtime + + Core Runtime consists of basic language-irrelevant runtime libraries, including ARK File, Tooling, and ARK Base. ARK File provides bytecodes. Tooling supports Debugger. ARK Base is responsible for implementing system calls. + +- Execution Engine + + The Execution Engine consists of an interpreter that executes bytecodes, Inline Caches that store hidden classes, and Profiler that analyzes and records runtime types. + +- ECMAScript Runtime + + ECMAScript Runtime consists of the JS object allocator, garbage collector \(GC\), and an internal library that supports ECMAScript specifications. + +- ARK Foreign Function Interface \(AFFI\) + + The AFFI provides a C++ function interface for ARK-JS runtime. + + +## Directory Structure + +``` +/ark +├── js_runtime # JS runtime module +├── runtime_core # Runtime common module +└── ts2abc # JS front-end tool of ARK compiler +``` + +## Note + +For details, see the note of each module. + +## Repositories Involved + +**[ARK Runtime Subsystem](ark-runtime-subsystem.md)** + +[ark/runtime\_core](https://gitee.com/openharmony/ark_runtime_core/blob/master/README.md) + +[ark/js\_runtime](https://gitee.com/openharmony/ark_js_runtime/blob/master/README.md) + +[ark/ts2abc](https://gitee.com/openharmony/ark_ts2abc/blob/master/README.md) + diff --git a/docs/development-example.md b/docs/development-example.md new file mode 100644 index 0000000000000000000000000000000000000000..242f2a735ddaaa4ec7053b706f90a6eeec278c6b --- /dev/null +++ b/docs/development-example.md @@ -0,0 +1,302 @@ +# Development Example + +- [HelloWorld](#section105987593810) +- [Performing Test Case Test262](#section118471435115815) + +This section describes how to develop and test ARK runtime. + +## HelloWorld + +Preparations + +1. Run the following command to compile ARK runtime: + +``` +./build.sh --product-name Hi3516DV300 --build-target ark_js_runtime +``` + +1. Run the following command to compile the ARK frontend: + +``` +./build.sh --product-name Hi3516DV300 --build-target ark_ts2abc_build +``` + +Note: Run the compilation commands in the project root directory. + +Running **hello-world.js** + +Create the **hello-world.js** file and write the following source code into the file: + +``` + print("Hello World!!!"); +``` + +Run the **hello-world.js** file. + +1. Use the ARK frontend to create the **hello-world.abc** file. + + ``` + node --expose-gc /your code path/out/ohos-arm-release/clang_x64/ark/ark/build/src/index.js hello-world.js + ``` + +2. Run the **hello-world.abc** file. + 1. Set the search path. + + ``` + export LD_LIBRARY_PATH=/your code path/prebuilts/clang/ohos/linux-x86_64/llvm/lib/:/your code path/out/ohos-arm-release/clang_x64/ark/ark:/your code path/out/ohos-arm-release/clang_x64/global/i18n_standard + ``` + + 2. Run **ark\_js\_vm**. + + ``` + /your code path/out/ohos-arm-release/clang_x64/ark/ark_js_runtime//ark_js_vm hello-world.abc _GLOBAL::func_main_0 + ``` + + The execution result is as follows: + + ``` + Hello World!!! + ``` + + + +>![](public_sys-resources/icon-note.gif) **NOTE:** +>In the preceding command, _your code path_ indicates the source code directory. + +Disassembling **hello-world.abc** + +Run the following command to export the result to the **output** file: + +``` +./your code path/out/ohos-arm-release/clang_x64/ark/ark/ark_disasm hello-world.abc output +``` + +The output is as follows: + +``` +# +# source binary: hello-world.abc +# + +# ==================== +# LITERALS + +# ==================== +# RECORDS + +.record _ESAnnotation + +.record _ESModuleMode { + u8 isModule +} + +# ==================== +# METHODS + +.function any func_main_0_any_any_any_any_(any a0, any a1, any a2) { + mov.dyn v2, a2 + mov.dyn v1, a1 + mov.dyn v0, a0 + builtin.acc + sta.dyn v5 + builtin.idi "print", 0x0 // Load the print function. + sta.dyn v3 + lda.str "Hello World!!!" // Load the Hello World!!! string. + sta.dyn v4 + builtin.tern3 v3, v4 // Call the print function. + builtin.acc +} +``` + +## Performing Test Case Test262 + +Preparations + +1. Run the following command to compile ARK runtime: + +``` +./build.sh --product-name Hi3516DV300 --build-target ark_js_runtime +``` + +1. Run the following command to compile the ARK frontend: + +``` +./build.sh --product-name Hi3516DV300 --build-target ark_ts2abc_build +``` + +Note: Run the compilation commands in the project root directory. + +Running Test262 + +Run the **run\_test262.py** script to download and run the Test262 test case. + +Command: + +``` +python3 test262/run_test262.py [options] +``` + +Run the script in _Project root directory_**/ark/ts2abc**. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Option

+

Description

+

--h, --help

+

Displays help information.

+

--dir DIR

+

Specifies the directory to test.

+

--file FILE

+

Specifies the file to test.

+

--mode [{1, 2, 3}]

+

Specifies the mode, which can be any of the following:

+
  • 1: default
  • 2: strict mode only
  • 3: default and strict modes
+

--es51

+

Runs Test262 ES5.1.

+

--es2015 [{all, only}]

+

Runs Test262 ES2015.

+

all: all cases

+

only: only ES2015

+

--esnext

+

Runs Test262-ES.next.

+

--engine FILE

+

Runs other engines and specifies binary files (such as d8, hermes, jsc, and qjs).

+

--babel

+

Specifies whether to use Babel to convert code.

+

--timeout TIMEOUT

+

Specifies the test timeout period in ms.

+

--threads THREADS

+

Specifies the number of concurrent threads.

+

--hostArgs HOSTARGS

+

Specifies the command line parameters sent to the eshost.

+

--ark-tool ARK_TOOL

+

Specifies the binary tool of ARK runtime.

+

--ark-frontend-tool ARK_FRONTEND_TOOL

+

Specifies the ARK front-end conversion tool.

+

--libs-dir LIBS_DIR

+

Specifies the set of .so dependency file paths, separated by colons (:).

+

--ark-frontend [{ts2panda, es2panda}]

+

Specifies the frontend.

+
+ +Example + +- Run test case ES51. + + ``` + python3 test262/run_test262.py --es51 + ``` + +- Run test case ES2015 only. + + ``` + python3 test262/run_test262.py --es2015 only + ``` + +- Run all ES2015 and ES51 test cases. + + ``` + python3 test262/run_test262.py --es2015 all + ``` + +- Run a test case. + + ``` + python3 test262/run_test262.py --file test262/data/test_es5/language/statements/break/12.8-1.js + ``` + +- Run all test cases in a directory. + + ``` + python3 test262/run_test262.py --dir test262/data/test_es5/language/statements + ``` + + +- Use Babel to convert a test case into ES5 and then run the test case. + + ``` + python3 test262/run_test262.py --babel --file test262/data/test_es5/language/statements/break/12.8-1.js + ``` + + +Test Output + +The results of all Test262 test cases are available in the **_Project root directory_/ark/ts2abc/out**. The test result in the shell is as follows: + +``` +$python3 test262/run_test262.py --file test262/data/test_es2015/built-ins/Array/15.4.5.1-5-1.js + +Wait a moment.......... +Test command: +node + test262/harness/bin/run.js + --hostType=panda + --hostPath=python3 + --hostArgs='-B test262/run_sunspider.py --ark-tool=/your code path/out/ohos-arm-release/clang_x64/ark/ark_js_runtime/ark_js_vm --ark-frontend-tool=/your code path/out/ohos-arm-release/clang_x64/ark/ark/build/src/index.js --libs-dir=/your code path/out/ohos-arm-release/clang_x64/ark/ark:/your code path/out/ohos-arm-release/clang_x64/global/i18n:/your code path/prebuilts/clang/ohos/linux-x86_64/llvm/lib/ --ark-frontend=ts2panda' + --threads=15 + --mode=only strict mode + --timeout=60000 + --tempDir=build/test262 + --test262Dir=test262/data + --saveCompiledTests + test262/data/test_es5/language/statements/break/12.8-1.js + +PASS test262/data/test_es2015/built-ins/Array/15.4.5.1-5-1.js (strict mode) +Ran 1 tests +1 passed +0 failed +used time is: 0:01:04.439642 +``` + diff --git a/docs/environment-setup-and-compilation.md b/docs/environment-setup-and-compilation.md new file mode 100644 index 0000000000000000000000000000000000000000..33b65881f970a5a31ad2abde96b232fa193adbf5 --- /dev/null +++ b/docs/environment-setup-and-compilation.md @@ -0,0 +1,41 @@ +# Environment Setup and Compilation + +- [Configuring the Environment](#section922419503415) +- [Compilation](#section1166711064317) + +## Configuring the Environment + +Use Ubuntu 18.04 or 20.04. For details about how to set up the environment, see: + +[Setting Up Ubuntu Development Environment with Installation Package and Building Source Code](https://gitee.com/openharmony/docs/blob/master/en/device-dev/quick-start/quickstart-standard-package-environment.md) + +## Compilation + +1. First compilation: + + ``` + ./build.sh --product-name Hi3516DV300 + ``` + +2. Compile an ARK runtime after the first compilation: + + ``` + ./build.sh --product-name Hi3516DV300 --build-target ark_js_runtime + ``` + +3. Compile the ARK frontend after the first compilation: + + ``` + ./build.sh --product-name Hi3516DV300 --build-target ark_ts2abc_build + ``` + + +The binary files related to ARK are available in the following paths: + +``` +out/ohos-arm-release/ark/ark/ +out/ohos-arm-release/ark/ark_js_runtime/ +out/ohos-arm-release/clang_x64/ark/ark/ +out/ohos-arm-release/clang_x64/ark/ark_js_runtime +``` + diff --git a/docs/figures/en-us_image_0000001149439242.png b/docs/figures/en-us_image_0000001149439242.png new file mode 100644 index 0000000000000000000000000000000000000000..b0b0cc58184baa1ecbd761eda982f3f3122d1208 Binary files /dev/null and b/docs/figures/en-us_image_0000001149439242.png differ diff --git a/docs/figures/en-us_image_0000001196789343.png b/docs/figures/en-us_image_0000001196789343.png new file mode 100644 index 0000000000000000000000000000000000000000..2f78e5f3e10a2b99c60ca31dc0f23ae5fb89f7a6 Binary files /dev/null and b/docs/figures/en-us_image_0000001196789343.png differ diff --git a/docs/figures/en-us_image_0000001197275269.png b/docs/figures/en-us_image_0000001197275269.png new file mode 100644 index 0000000000000000000000000000000000000000..2f78e5f3e10a2b99c60ca31dc0f23ae5fb89f7a6 Binary files /dev/null and b/docs/figures/en-us_image_0000001197275269.png differ diff --git a/docs/figures/en-us_image_0000001197967897.png b/docs/figures/en-us_image_0000001197967897.png new file mode 100644 index 0000000000000000000000000000000000000000..e8e9951f3f2dc5b566a3d9b36229def46c41aecd Binary files /dev/null and b/docs/figures/en-us_image_0000001197967897.png differ diff --git a/docs/figures/en-us_image_0000001197967983.png b/docs/figures/en-us_image_0000001197967983.png new file mode 100644 index 0000000000000000000000000000000000000000..e8e9951f3f2dc5b566a3d9b36229def46c41aecd Binary files /dev/null and b/docs/figures/en-us_image_0000001197967983.png differ diff --git a/docs/figures/zh-cn_image_0000001196712959.png b/docs/figures/zh-cn_image_0000001196712959.png new file mode 100644 index 0000000000000000000000000000000000000000..2f78e5f3e10a2b99c60ca31dc0f23ae5fb89f7a6 Binary files /dev/null and b/docs/figures/zh-cn_image_0000001196712959.png differ diff --git a/docs/figures/zh-cn_image_0000001196789343.png b/docs/figures/zh-cn_image_0000001196789343.png new file mode 100644 index 0000000000000000000000000000000000000000..2f78e5f3e10a2b99c60ca31dc0f23ae5fb89f7a6 Binary files /dev/null and b/docs/figures/zh-cn_image_0000001196789343.png differ diff --git a/docs/figures/zh-cn_image_0000001197275269.png b/docs/figures/zh-cn_image_0000001197275269.png new file mode 100644 index 0000000000000000000000000000000000000000..2f78e5f3e10a2b99c60ca31dc0f23ae5fb89f7a6 Binary files /dev/null and b/docs/figures/zh-cn_image_0000001197275269.png differ diff --git a/docs/figures/zh-cn_image_0000001197967897.png b/docs/figures/zh-cn_image_0000001197967897.png new file mode 100644 index 0000000000000000000000000000000000000000..e8e9951f3f2dc5b566a3d9b36229def46c41aecd Binary files /dev/null and b/docs/figures/zh-cn_image_0000001197967897.png differ diff --git a/docs/figures/zh-cn_image_0000001197967983.png b/docs/figures/zh-cn_image_0000001197967983.png new file mode 100644 index 0000000000000000000000000000000000000000..e8e9951f3f2dc5b566a3d9b36229def46c41aecd Binary files /dev/null and b/docs/figures/zh-cn_image_0000001197967983.png differ diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 0000000000000000000000000000000000000000..8d728459bc98ea0a3171e1b19fcafc1b7aafa6b2 --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,53 @@ +# Overview + +ARK is a unified programming platform developed by Huawei. Its key components include a compiler, toolchain, and runtime. ARK supports compilation and running of high-level programming languages on the multi-chip platform and accelerates the running of the OpenHarmony standard operating system and its applications and services on mobile phones, PCs, tablets, TVs, automobiles, and smart wearables. The ARK-JS open sourced this time provides the capability of compiling and running the JavaScript \(JS\) language on the OpenHarmony operating system. + +The ARK-JS consists of two parts: JS compiler toolchain and JS runtime. The JS compiler toolchain compiles JS source code into ARK bytecodes. The JS runtime executes the generated ARK bytecodes. Unless otherwise specified, bytecodes refer to ARK bytecodes in this document. + +The following figure shows the architecture of the JS compiler toolchain. + +![](figures/en-us_image_0000001197967897.png) + +The ARK-JS source code compiler receives the JS source code, and ts2abc converts the JS source code into an abc file. + +The following figure shows the JS runtime architecture. + +![](figures/en-us_image_0000001196789343.png) + +ARK-JS Runtime runs ARK bytecode files to implement JS semantic logic. + +ARK-JS Runtime consists of the following: + +- Core Runtime + + Core Runtime consists of basic language-irrelevant runtime libraries, including ARK File, Tooling, and ARK Base. ARK File provides bytecodes. Tooling supports Debugger. ARK Base is responsible for implementing system calls. + +- Execution Engine + + The Execution Engine consists of an interpreter that executes bytecodes, Inline Caches that store hidden classes, and Profiler that analyzes and records runtime types. + +- ECMAScript Runtime + + ECMAScript Runtime consists of the JS object allocator, garbage collector \(GC\), and an internal library that supports ECMAScript specifications. + +- ARK Foreign Function Interface \(AFFI\) + + The AFFI provides a C++ function interface for ARK-JS runtime. + + +Future plan: + +- High-performance TypeScript support + +OpenHamony uses TypeScript \(TS\) as one of its main development languages. TS is simply JS with syntax for static types. The common way to process TS in the industry is to convert TS into JS and execute JS code with JS runtime. + +ARK-JS is planned to support the native TS. When compiling the TS source code, ts2abc analyzes and obtains the TS type information and sends the TS type information to ARK-JS runtime. The ARK-JS runtime directly uses the type information to statically generate an inline cache to accelerate bytecode execution. + +The TS Ahead of Time \(AOT\) compiler directly converts the source code into high-quality machine code based on the TS type information sent from ts2abc, which greatly improves the running performance. + +- Lightweight Actor concurrency model + +ECMAScript does not provide concurrency specifications. The Actor concurrency model is used in the JS engines in the industry to implement concurrent processing. In this model, executors do not share data and communicate with each other using the messaging mechanism. The JS Actor model \(web-worker\) in the industry has defects such as slow startup and high memory usage. ARK-JS is required to provide the Actor implementation that features fast startup and low memory usage to better leverage the device's multi-core feature to improve performance. + +ARK-JS is planned to share immutable objects, built-in code blocks, and method bytecodes in Actor instances based on the Actor memory isolation model to accelerate the startup of JS Actor and reduce memory overhead and implement the lightweight Actor concurrency model. + diff --git a/docs/using-the-toolchain.md b/docs/using-the-toolchain.md new file mode 100644 index 0000000000000000000000000000000000000000..2dbbf9db70cb04446cc3cda0e5987c5fbab79bce --- /dev/null +++ b/docs/using-the-toolchain.md @@ -0,0 +1,237 @@ +# Using the Toolchain + +The ARK front-end tools use the command line interaction mode and convert JS code into ARK bytecodes that can run on ARK runtime. The toolchain supports Windows, Linux, and macOS. + +JS Bytecode Compiler + +Converts a JS file into ARK bytecodes. + +Command: + +``` +node --expose-gc index.js [option] file.js +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Option

+

Abbreviation

+

Description

+

Value Range

+

Default Value

+

--modules

+

-m

+

Compiles JS files based on the module.

+

-

+

-

+

--debug-log

+

-l

+

Enables the log function.

+

-

+

-

+

--dump-assembly

+

-a

+

Outputs a text ARK bytecode file.

+

-

+

-

+

--debug

+

-d

+

Provides debug information.

+

-

+

-

+

--show-statistics

+

-s

+

Displays statistics about bytecodes.

+

-

+

-

+

--output

+

-o

+

Specifies the path of the output file.

+

-

+

-

+

--timeout

+

-t

+

Specifies the timeout threshold.

+

-

+

-

+

--help

+

-h

+

Displays help information.

+

-

+

-

+

--bc-version

+

-v

+

Outputs the current bytecode version.

+

-

+

-

+

--bc-min-version

+
  

Outputs the lowest bytecode version supported.

+

-

+

-

+
+ +Assembler ark\_asm + +The ark\_asm assembler converts the text ARK bytecode file into a bytecode file in binary format. + +Command: + +``` +ark_asm [Option] Input file Output file +``` + + + + + + + + + + + + + + + + + + + + + + + + + +

Option

+

Description

+

--dump-scopes

+

Saves the result to a JSON file to support the debug mode in Visual Studio Code.

+

--help

+

Displays help information.

+

--log-file

+

Specifies the log file output path after log printing is enabled.

+

--optimize

+

Enables compilation optimization.

+

--size-stat

+

Collects statistics on and prints ARK bytecode information after conversion.

+

--verbose

+

Enables log printing.

+
+ +Input file: ARK bytecodes in text format + +Output file: ARK bytecodes in binary format + +Disassembler ark\_dissam + +The ark\_dissam disassembler converts binary ARK bytecodes into readable text ARK bytecodes. + +Command: + +``` +ark_dissam [Option] Input file Output file +``` + + + + + + + + + + + + + + + + + + + +

Option

+

Description

+

--debug

+

Enables the function for printing debug information.

+

--debug-file

+

Specifies the path of the debug information output file. The default value is std::cout.

+

--help

+

Displays help information.

+

--verbose

+

Outputs the comments of the output file.

+
+ +Input file: ARK bytecodes in binary format + +Output file: ARK bytecodes in text format + diff --git "a/docs/\345\267\245\345\205\267\351\223\276\344\275\277\347\224\250.md" "b/docs/\345\267\245\345\205\267\351\223\276\344\275\277\347\224\250.md" new file mode 100644 index 0000000000000000000000000000000000000000..9466b7f97350cff5b6c1aec7d375f24d6dec6c08 --- /dev/null +++ "b/docs/\345\267\245\345\205\267\351\223\276\344\275\277\347\224\250.md" @@ -0,0 +1,237 @@ +# 工具链使用 + +方舟前端工具采用命令行交互方式,支持将JS代码转换为方舟字节码,使其能够在方舟运行时上运行。支持Windows/Linux/Mac平台。 + +JS字节码编译工具概述 + +将JS文件转换为方舟字节码。 + +命令行格式: + +``` +node --expose-gc index.js [选项] file.js +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

选项

+

缩写

+

描述

+

取值范围

+

默认值

+

--modules

+

-m

+

按照Module方式编译

+

-

+

-

+

--debug-log

+

-l

+

使能log信息

+

-

+

-

+

--dump-assembly

+

-a

+

输出为可读文本格式的字节码文件

+

-

+

-

+

--debug

+

-d

+

携带debug信息

+

-

+

-

+

--show-statistics

+

-s

+

显示字节码相关的统计信息

+

-

+

-

+

--output

+

-o

+

输出文件路径

+

-

+

-

+

--timeout

+

-t

+

超时门限

+

-

+

-

+

--help

+

-h

+

帮助提示

+

-

+

-

+

--bc-version

+

-v

+

输出当前字节码版本

+

-

+

-

+

--bc-min-version

+
  

输出支持的最低字节码版本

+

-

+

-

+
+ +汇编器工具概述 + +工具名称为ark\_asm,用于将文本格式的方舟字节码文件转换为二进制格式的方舟字节码文件。 + +命令行格式: + +``` +ark_asm [选项] 输入文件 输出文件 +``` + + + + + + + + + + + + + + + + + + + + + + + + + +

选项

+

描述

+

--dump-scopes

+

将结果保存到json文件中,以支持在VS Code中的debug模式

+

--help

+

帮助提示

+

--log-file

+

使能log打印后,指定log文件输出路径

+

--optimize

+

使能编译优化

+

--size-stat

+

统计并打印出转换后方舟字节码信息

+

--verbose

+

使能log打印

+
+ +输入文件:文本格式的方舟字节码 + +输出文件:二进制格式的方舟字节码 + +反汇编器工具概述 + +工具名称为ark\_dissam,用于将二进制格式的方舟字节码文件转换为文本格式的方舟字节码文件。 + +命令行格式: + +``` +ark_dissam [选项] 输入文件 输出文件 +``` + + + + + + + + + + + + + + + + + + + +

选项

+

描述

+

--debug

+

使能调试信息

+

--debug-file

+

调试信息输出文件路径,默认为std::cout

+

--help

+

帮助提示

+

--verbose

+

增加输出文件的注释信息

+
+ +输入文件:二进制格式的方舟字节码 + +输出文件:文本格式的方舟字节码 + diff --git "a/docs/\345\274\200\345\217\221\345\256\236\344\276\213.md" "b/docs/\345\274\200\345\217\221\345\256\236\344\276\213.md" new file mode 100644 index 0000000000000000000000000000000000000000..6527605b79e9760c36ebbd205f2c0ebf1084f22b --- /dev/null +++ "b/docs/\345\274\200\345\217\221\345\256\236\344\276\213.md" @@ -0,0 +1,299 @@ +# 开发实例 + +- [HelloWorld](#section105987593810) +- [运行Test262测试用例](#section118471435115815) + +本章节将介绍基于方舟运行时的开发测试实例。 + +## HelloWorld + +运行前准备 + +1. 编译方舟运行时,编译命令: + +``` +./build.sh --product-name Hi3516DV300 --build-target ark_js_runtime +``` + +1. 编译方舟前端,编译命令: + +``` +./build.sh --product-name Hi3516DV300 --build-target ark_ts2abc_build +``` + +_注:编译命令执行路径为项目根目录。_ + +运行hello-world.js + +新建hello-world.js文件,写入以下源码: + +``` + print("Hello World!!!"); +``` + +运行步骤: + +1. 通过方舟前端生成hello-world.abc文件,编译命令: + + ``` + node --expose-gc /your code path/out/ohos-arm-release/clang_x64/ark/ark/build/src/index.js hello-world.js + ``` + +2. 执行hello-world.abc文件: + 1. 设置搜索路径: + + ``` + export LD_LIBRARY_PATH=/your code path/prebuilts/clang/ohos/linux-x86_64/llvm/lib/:/your code path/out/ohos-arm-release/clang_x64/ark/ark:/your code path/out/ohos-arm-release/clang_x64/global/i18n_standard + ``` + + 2. 执行ark\_js\_vm: + + ``` + /your code path/out/ohos-arm-release/clang_x64/ark/ark_js_runtime//ark_js_vm hello-world.abc _GLOBAL::func_main_0 + ``` + + 执行结果如下: + + ``` + Hello World!!! + ``` + + + +>![](public_sys-resources/icon-note.gif) **说明:** +>此处“_your code path_”为源码目录路径。 + +反汇编hello-world.abc + +执行如下命令,结果输出到output文件中: + +``` +./your code path/out/ohos-arm-release/clang_x64/ark/ark/ark_disasm hello-world.abc output +``` + +hello-world.abc反汇编结果如下: + +``` +# +# source binary: hello-world.abc +# + +# ==================== +# LITERALS + +# ==================== +# RECORDS + +.record _ESAnnotation + +.record _ESModuleMode { + u8 isModule +} + +# ==================== +# METHODS + +.function any func_main_0_any_any_any_any_(any a0, any a1, any a2) { + mov.dyn v2, a2 + mov.dyn v1, a1 + mov.dyn v0, a0 + builtin.acc + sta.dyn v5 + builtin.idi "print", 0x0 // 加载print函数 + sta.dyn v3 + lda.str "Hello World!!!" // 加载Hello World!!!字符串 + sta.dyn v4 + builtin.tern3 v3, v4 // 调用print函数 + builtin.acc +} +``` + +## 运行Test262测试用例 + +运行前准备 + +1. 编译方舟运行时,编译命令: + +``` +./build.sh --product-name Hi3516DV300 --build-target ark_js_runtime +``` + +1. 编译方舟前端,编译命令: + +``` +./build.sh --product-name Hi3516DV300 --build-target ark_ts2abc_build +``` + +_注:编译命令执行路径为项目根目录。_ + +运行Test262 + +运行run\_test262.py脚本,下载及运行Test262用例。 + +命令行格式: + +``` +python3 test262/run_test262.py [options] +``` + +执行路径为:项目根目录/ark/ts2abc。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

选项

+

描述

+

--h,--help

+

帮助提示

+

--dir DIR

+

选定要测试的目录

+

--file FILE

+

选定要测试的文件

+

--mode [{1, 2, 3}]

+

模式选择,1:仅默认值;2:仅严格模式;3:默认模式和严格模式

+

--es51

+

运行Test262 ES5.1版本

+

--es2015 [{all, only}]

+

运行Test262 ES2015版本,all:包含的所有用例;only:仅包括ES2015

+

--esnext

+

运行Test262-ES.next

+

--engine FILE

+

运行测试的其他引擎,指定二进制文件(如:d8,hermes,jsc,qjs...)

+

--babel

+

是否使用Babel转换

+

--timeout TIMEOUT

+

设置测试超时时间(以毫秒为单位)

+

--threads THREADS

+

设置并行运行线程数

+

--hostArgs HOSTARGS

+

传递给eshost主机的命令行参数

+

--ark-tool ARK_TOOL

+

方舟运行时的二进制工具

+

--ark-frontend-tool ARK_FRONTEND_TOOL

+

方舟前端转换工具

+

--libs-dir LIBS_DIR

+

依赖so的路径集合,通过“:”分割

+

--ark-frontend [{ts2panda, es2panda}]

+

指定前端

+
+ +测试运行示例 + +- 运行ES51测试用例: + + ``` + python3 test262/run_test262.py --es51 + ``` + +- 仅运行ES2015测试用: + + ``` + python3 test262/run_test262.py --es2015 only + ``` + +- 运行ES2015和ES51所有测试用例: + + ``` + python3 test262/run_test262.py --es2015 all + ``` + +- 运行单一测试用例: + + ``` + python3 test262/run_test262.py --file test262/data/test_es5/language/statements/break/12.8-1.js + ``` + +- 运行某目录下所有测试用例: + + ``` + python3 test262/run_test262.py --dir test262/data/test_es5/language/statements + ``` + + +- 使用\`babel\`把单个测试用例转换成es5后再运行: + + ``` + python3 test262/run_test262.py --babel --file test262/data/test_es5/language/statements/break/12.8-1.js + ``` + + +测试输出 + +Test262所有用例的测试结果位于项目根目录/ark/ts2abc/out下。shell中测试输出结果如下: + +``` +$python3 test262/run_test262.py --file test262/data/test_es2015/built-ins/Array/15.4.5.1-5-1.js + +Wait a moment.......... +Test command: +node + test262/harness/bin/run.js + --hostType=panda + --hostPath=python3 + --hostArgs='-B test262/run_sunspider.py --ark-tool=/your code path/out/ohos-arm-release/clang_x64/ark/ark_js_runtime/ark_js_vm --ark-frontend-tool=/your code path/out/ohos-arm-release/clang_x64/ark/ark/build/src/index.js --libs-dir=/your code path/out/ohos-arm-release/clang_x64/ark/ark:/your code path/out/ohos-arm-release/clang_x64/global/i18n:/your code path/prebuilts/clang/ohos/linux-x86_64/llvm/lib/ --ark-frontend=ts2panda' + --threads=15 + --mode=only strict mode + --timeout=60000 + --tempDir=build/test262 + --test262Dir=test262/data + --saveCompiledTests + test262/data/test_es5/language/statements/break/12.8-1.js + +PASS test262/data/test_es2015/built-ins/Array/15.4.5.1-5-1.js (strict mode) +Ran 1 tests +1 passed +0 failed +used time is: 0:01:04.439642 +``` + diff --git "a/docs/\346\226\271\350\210\237\350\277\220\350\241\214\346\227\266\344\275\277\347\224\250\346\214\207\345\215\227.md" "b/docs/\346\226\271\350\210\237\350\277\220\350\241\214\346\227\266\344\275\277\347\224\250\346\214\207\345\215\227.md" new file mode 100644 index 0000000000000000000000000000000000000000..421cadd9840dabb8d51a79b8b990c8baea65afdd --- /dev/null +++ "b/docs/\346\226\271\350\210\237\350\277\220\350\241\214\346\227\266\344\275\277\347\224\250\346\214\207\345\215\227.md" @@ -0,0 +1,7 @@ +# 方舟运行时使用指南 + +- [综述](综述.md) +- [环境搭建和编译](环境搭建和编译.md) +- [开发实例](开发实例.md) +- [工具链使用](工具链使用.md) + diff --git "a/docs/\346\226\271\350\210\237\350\277\220\350\241\214\346\227\266\345\255\220\347\263\273\347\273\237.md" "b/docs/\346\226\271\350\210\237\350\277\220\350\241\214\346\227\266\345\255\220\347\263\273\347\273\237.md" new file mode 100644 index 0000000000000000000000000000000000000000..0e39e4a4ceea1101b91273f5cdb0a97e3a770228 --- /dev/null +++ "b/docs/\346\226\271\350\210\237\350\277\220\350\241\214\346\227\266\345\255\220\347\263\273\347\273\237.md" @@ -0,0 +1,67 @@ +# 方舟运行时子系统 + +- [简介](#section11660541593) +- [目录](#section161941989596) +- [说明](#section18393638195820) +- [相关仓](#section1371113476307) + +## 简介 + +方舟\(ARK\)是华为自研的统一编程平台,包含编译器、工具链、运行时等关键部件,支持高级语言在多种芯片平台的编译与运行,并支撑OpenHarmony操作系统及其应用和服务运行在手机、个人电脑、平板、电视、汽车和智能穿戴等多种设备上的需求。本次开源的ARK-JS提供的能力是在OpenHarmony操作系统中编译和运行JavaScript语言\(本文后面简称JS\)。 + +本次开源的ARK-JS分成两个部分,分别是JS编译工具链与JS运行时。JS工具链将JS源码编译成方舟字节码\(ARK Bytecode\),JS运行时负责执行生成的方舟字节码\(后续如无特殊说明,字节码特指方舟字节码\)。 + +JS编译工具链架构: + +![](figures/zh-cn_image_0000001197967983.png) + +js前端编译器,将JavaScript源码解析为AST后,经过AST变换、字节码生成器、寄存器分配后由native emiter产生方舟字节码文件\(abc文件\) + +JS运行时(Runtime)架构: + +![](figures/zh-cn_image_0000001197275269.png) + +ARK-JS Runtime以方舟字节码文件作为输入并直接运行字节码文件,实现对应的JS语义逻辑。 + +ARK-JS Runtime主要由四个部分组成: + +- Core Runtime + + Core Runtime主要由语言无关的基础运行库组成,包括承载字节码的ARK File组件、支持Debugger的Tooling组件、负责对应系统调用的ARK Base组件等。 + +- Execution Engine + + 执行引擎目前包含执行字节码的解释器、缓存隐藏类和内联缓存、以及剖析记录运行时类型的Profiler。 + +- ECMAScript Runtime + + ECMAScript Runtime则包含了各种JS对象的分配器、垃圾回收器、以及用以支撑ECMAScript规范的内部运行库。 + +- AFFI \(ARK Foreign Function Interface\) + + AFFI是ARK JS运行时的C++语言外部函数接口。 + + +## 目录 + +``` +/ark +├── js_runtime # JS运行时组件 +├── runtime_core # 运行时公共组件 +└── ts2abc # 方舟编译器中JavaScript语言的前端工具 +``` + +## 说明 + +见各组件说明 + +## 相关仓 + +**[方舟运行时子系统](方舟运行时子系统.md)** + +[ark/runtime\_core](https://gitee.com/openharmony/ark_runtime_core/blob/master/README_zh.md) + +[ark/js\_runtime](https://gitee.com/openharmony/ark_js_runtime/blob/master/README_zh.md) + +[ark/ts2abc](https://gitee.com/openharmony/ark_ts2abc/blob/master/README_zh.md) + diff --git "a/docs/\347\216\257\345\242\203\346\220\255\345\273\272\345\222\214\347\274\226\350\257\221.md" "b/docs/\347\216\257\345\242\203\346\220\255\345\273\272\345\222\214\347\274\226\350\257\221.md" new file mode 100644 index 0000000000000000000000000000000000000000..7a104e0de6f30a447e25eb6fa15c5c2947228bd6 --- /dev/null +++ "b/docs/\347\216\257\345\242\203\346\220\255\345\273\272\345\222\214\347\274\226\350\257\221.md" @@ -0,0 +1,41 @@ +# 环境搭建和编译 + +- [环境配置](#section922419503415) +- [代码编译](#section1166711064317) + +## 环境配置 + +Ubuntu版本要求18.04或20.04,详细环境搭建参考: + +[搭建Ubuntu环境及编译(安装包方式)](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-standard-package-environment.md) + +## 代码编译 + +1. 首次编译: + + ``` + ./build.sh --product-name Hi3516DV300 + ``` + +2. 首次编译后增量编译方舟运行时: + + ``` + ./build.sh --product-name Hi3516DV300 --build-target ark_js_runtime + ``` + +3. 首次编译后增量编译方舟前端: + + ``` + ./build.sh --product-name Hi3516DV300 --build-target ark_ts2abc_build + ``` + + +方舟相关的二进制文件在如下路径: + +``` +out/ohos-arm-release/ark/ark/ +out/ohos-arm-release/ark/ark_js_runtime/ +out/ohos-arm-release/clang_x64/ark/ark/ +out/ohos-arm-release/clang_x64/ark/ark_js_runtime +``` + diff --git "a/docs/\347\273\274\350\277\260.md" "b/docs/\347\273\274\350\277\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..f324f3d9304de9ac6bdd2ea5776022d15181be6a --- /dev/null +++ "b/docs/\347\273\274\350\277\260.md" @@ -0,0 +1,53 @@ +# 综述 + +方舟\(ARK\)是华为自研的统一编程平台,包含编译器、工具链、运行时等关键部件,支持高级语言在多种芯片平台的编译与运行,并支撑OpenHarmony标准操作系统及其应用和服务运行在手机、个人电脑、平板、电视、汽车和智能穿戴等多种设备上的需求。本次开源的ARK-JS提供的能力是在OpenHarmony操作系统中编译和运行JavaScript语言\(本文后面简称JS\)。 + +本次开源的ARK-JS分成两个部分,分别是JS编译工具链与JS运行时。JS工具链将JS源码编译成方舟字节码\(ARK Bytecode\),JS运行时负责执行生成的方舟字节码\(后续如无特殊说明,字节码特指方舟字节码\)。 + +JS编译工具链架构: + +![](figures/zh-cn_image_0000001197967897.png) + +ARK-JS的源码编译器接收JS源码的输入,再由ts2abc(将JavaScript文件转换为字节码的工具)转化为abc文件。 + +JS运行时(Runtime)架构: + +![](figures/zh-cn_image_0000001196789343.png) + +ARK-JS Runtime以方舟字节码文件作为输入并直接运行字节码文件,实现对应的JS语义逻辑。 + +ARK-JS Runtime主要由四个部分组成: + +- Core Runtime + + Core Runtime主要由语言无关的基础运行库组成,包括承载字节码的ARK File组件、支持Debugger的Tooling组件、负责对应系统调用的ARK Base组件等。 + +- Execution Engine + + 执行引擎目前包含执行字节码的解释器、缓存隐藏类和内联缓存、以及剖析记录运行时类型的Profiler。 + +- ECMAScript Runtime + + ECMAScript Runtime则包含了各种JS对象的分配器、垃圾回收器、以及用以支撑ECMAScript规范的内部运行库。 + +- AFFI \(ARK Foreign Function Interface\) + + AFFI是ARK-JS运行时的C++语言外部函数接口。 + + +未来规划: + +- 高性能TypeScript支持 + +OpenHamony目前选用了TS(TypeScript)作为主要开发语言之一,而TS简单地概括就是具有静态类型的JS。业界通用的执行方式是把TS转化为JS,再使用JS运行时来执行生成的JS代码。 + +ARK-JS规划原生支持TS。在ts2abc编译TS源码时,会推导分析TS的类型信息并传递给ARK-JS运行时,运行时直接利用类型信息静态生成内联缓存从而加速字节码执行。 + +TS AOT \(Ahead of Time\) Compiler,利用ts2abc传递的类型信息,直接编译生成高质量的机器码,使得应用可以直接以机器码形式运行,提升运行性能。 + +- 轻量级Actor并发模型 + +ECMAScript没有提供并发规范,业界JS引擎的实现中常用Actor并发模型。此模型下执行体之间不共享任何数据,通过消息机制进行通信。业界当前实现的JS Actor模型(web-worker)有启动速度慢、内存占用高这些缺陷。为了利用设备的多核能力获得更好的性能提升,ARK-JS需要提供启动快、内存占用少的Actor实现。 + +ARK-JS规划在Actor内存隔离模型的基础上,共享actor实例中的不可变或者不易变的对象、内建代码块、方法字节码等,来提升JS Actor的启动性能和节省内存开销,达到实现轻量级Actor并发模型的目标。 + diff --git a/ecmascript/accessor_data.h b/ecmascript/accessor_data.h new file mode 100644 index 0000000000000000000000000000000000000000..35633dd9b62b6e14bcbaa5753363b7e88e598b15 --- /dev/null +++ b/ecmascript/accessor_data.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMA_ACCESSOR_DATA_H +#define PANDA_RUNTIME_ECMA_ACCESSOR_DATA_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/record.h" + +namespace panda::ecmascript { +class AccessorData final : public Record { +public: + using InternalGetFunc = JSTaggedValue (*)(JSThread *, const JSHandle &); + using InternalSetFunc = bool (*)(JSThread *, const JSHandle &, const JSHandle &, bool); + + static AccessorData *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsAccessorData() || JSTaggedValue(object).IsInternalAccessor()); + return static_cast(object); + } + + inline bool IsInternal() const + { + return GetClass()->IsInternalAccessor(); + } + + inline bool HasSetter() const + { + return !GetSetter().IsUndefined(); + } + + JSTaggedValue CallInternalGet(JSThread *thread, const JSHandle &obj) const + { + ASSERT(GetGetter().IsJSNativePointer()); + JSNativePointer *getter = JSNativePointer::Cast(GetGetter().GetTaggedObject()); + auto getFunc = reinterpret_cast(getter->GetExternalPointer()); + return getFunc(thread, obj); + } + + bool CallInternalSet(JSThread *thread, const JSHandle &obj, const JSHandle &value, + bool mayThrow = false) const + { + ASSERT(GetSetter().IsJSNativePointer()); + JSNativePointer *setter = JSNativePointer::Cast(GetSetter().GetTaggedObject()); + auto setFunc = reinterpret_cast(setter->GetExternalPointer()); + return setFunc(thread, obj, value, mayThrow); + } + + static constexpr size_t GETTER_OFFSET = Record::SIZE; + ACCESSORS(Getter, GETTER_OFFSET, SETTER_OFFSET); + ACCESSORS(Setter, SETTER_OFFSET, SIZE); + + DECL_DUMP() + DECL_VISIT_OBJECT(GETTER_OFFSET, SIZE) +}; + +class CompletionRecord final : public Record { +public: + enum : uint8_t { NORMAL = 0U, BREAK, CONTINUE, RETURN, THROW }; + + static CompletionRecord *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsCompletionRecord()); + return static_cast(object); + } + + bool IsThrow() const + { + return JSTaggedValue::SameValue(this->GetType(), JSTaggedValue(static_cast(THROW))); + } + + static constexpr size_t TYPE_OFFSET = ObjectHeaderSize(); + ACCESSORS(Type, TYPE_OFFSET, VALUE_OFFSET); + ACCESSORS(Value, VALUE_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(TYPE_OFFSET, SIZE) +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMA_ACCESSOR_DATA_H diff --git a/ecmascript/base/array_helper.cpp b/ecmascript/base/array_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e01a821c7141a82d2c1c9e72370c095dfeb65ae0 --- /dev/null +++ b/ecmascript/base/array_helper.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/array_helper.h" + +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_tagged_value-inl.h" + +namespace panda::ecmascript::base { +bool ArrayHelper::IsConcatSpreadable(JSThread *thread, const JSHandle &obj) +{ + // 1. If Type(O) is not Object, return false. + if (!obj->IsECMAObject()) { + return false; + } + + // 2. Let spreadable be Get(O, @@isConcatSpreadable). + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + JSHandle isConcatsprKey = env->GetIsConcatSpreadableSymbol(); + JSHandle spreadable = JSTaggedValue::GetProperty(thread, obj, isConcatsprKey).GetValue(); + // 3. ReturnIfAbrupt(spreadable). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 4. If spreadable is not undefined, return ToBoolean(spreadable). + if (!spreadable->IsUndefined()) { + return spreadable->ToBoolean(); + } + + // 5. Return IsArray(O). + return obj->IsArray(thread); +} + +int32_t ArrayHelper::SortCompare(JSThread *thread, const JSHandle &callbackfnHandle, + const JSHandle &valueX, const JSHandle &valueY, + const JSHandle &argv) +{ + // 1. If x and y are both undefined, return +0. + if (valueX->IsHole()) { + if (valueY->IsHole()) { + return 0; + } + return 1; + } + if (valueY->IsHole()) { + return -1; + } + if (valueX->IsUndefined()) { + if (valueY->IsUndefined()) { + return 0; + } + // 2. If x is undefined, return 1. + return 1; + } + // 3. If y is undefined, return -1. + if (valueY->IsUndefined()) { + return -1; + } + // 4. If the argument comparefn is not undefined, then + // a. Let v be ToNumber(Call(comparefn, undefined, «x, y»)). + // b. ReturnIfAbrupt(v). + // c. If v is NaN, return +0. + // d. Return v. + if (!callbackfnHandle->IsUndefined()) { + JSHandle thisArgHandle(thread, JSTaggedValue::Undefined()); + argv->Set(thread, 0, valueX); + argv->Set(thread, 1, valueY); + JSTaggedValue callResult = JSFunction::Call(thread, callbackfnHandle, thisArgHandle, argv); + if (callResult.IsInt()) { + return callResult.GetInt(); + } + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + JSHandle testResult(thread, callResult); + JSTaggedNumber v = JSTaggedValue::ToNumber(thread, testResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + double value = v.GetNumber(); + if (std::isnan(value)) { + return +0; + } + return static_cast(value); + } + // 5. Let xString be ToString(x). + // 6. ReturnIfAbrupt(xString). + // 7. Let yString be ToString(y). + // 8. ReturnIfAbrupt(yString). + // 9. If xString < yString, return -1. + // 10. If xString > yString, return 1. + // 11. Return +0. + if (valueX->IsInt() && valueY->IsInt()) { + auto xNumber = JSTaggedNumber(valueX.GetTaggedValue()); + auto yNumber = JSTaggedNumber(valueY.GetTaggedValue()); + return xNumber.GetInt() > yNumber.GetInt() ? 1 : 0; + } + ComparisonResult compareResult; + if (valueX->IsDouble() && valueY->IsDouble() && !std::isinf(valueX->GetDouble()) && + !std::isinf(valueY->GetDouble())) { + auto xNumber = JSTaggedNumber(valueX.GetTaggedValue()); + auto yNumber = JSTaggedNumber(valueY.GetTaggedValue()); + compareResult = JSTaggedValue::StrictNumberCompare(xNumber.GetDouble(), yNumber.GetDouble()); + return compareResult == ComparisonResult::GREAT ? 1 : 0; + } + JSHandle xValueHandle(JSTaggedValue::ToString(thread, valueX)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + JSHandle yValueHandle(JSTaggedValue::ToString(thread, valueY)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + compareResult = JSTaggedValue::Compare(thread, xValueHandle, yValueHandle); + return compareResult == ComparisonResult::GREAT ? 1 : 0; +} + +double ArrayHelper::GetLength(JSThread *thread, const JSHandle &thisHandle) +{ + if (thisHandle->IsJSArray()) { + return JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength(); + } + if (thisHandle->IsTypedArray()) { + return TypedArrayHelper::GetArrayLength(thread, JSHandle::Cast(thisHandle)); + } + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle lenResult = JSTaggedValue::GetProperty(thread, thisHandle, lengthKey).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + JSTaggedNumber len = JSTaggedValue::ToLength(thread, lenResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + return len.GetNumber(); +} + +double ArrayHelper::GetArrayLength(JSThread *thread, const JSHandle &thisHandle) +{ + if (thisHandle->IsJSArray()) { + return JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength(); + } + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle lenResult = JSTaggedValue::GetProperty(thread, thisHandle, lengthKey).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + JSTaggedNumber len = JSTaggedValue::ToLength(thread, lenResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + return len.GetNumber(); +} +} // namespace panda::ecmascript::base diff --git a/ecmascript/base/array_helper.h b/ecmascript/base/array_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..9dfa9cc2fe09395b985f64924ca5c87578bc89ed --- /dev/null +++ b/ecmascript/base/array_helper.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BASE_ARRAY_HELPER_H +#define PANDA_RUNTIME_ECMASCRIPT_BASE_ARRAY_HELPER_H + +#include +#include + +#include "ecmascript/base/builtins_base.h" + +namespace panda::ecmascript::base { +class ArrayHelper { +public: + static bool IsConcatSpreadable(JSThread *thread, const JSHandle &obj); + static int32_t SortCompare(JSThread *thread, const JSHandle &callbackfnHandle, + const JSHandle &valueX, const JSHandle &valueY, + const JSHandle &arg); + static double GetLength(JSThread *thread, const JSHandle &thisHandle); + static double GetArrayLength(JSThread *thread, const JSHandle &thisHandle); +}; +} // namespace panda::ecmascript::base + +#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_ARRAY_HELPER_H diff --git a/ecmascript/base/builtins_base.cpp b/ecmascript/base/builtins_base.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bdcef5dc508176acc6744feb655d4b4a1b52dda5 --- /dev/null +++ b/ecmascript/base/builtins_base.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript::base { +JSHandle BuiltinsBase::GetArgsArray(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + array_size_t length = msg->GetArgsNumber(); + JSHandle array = factory->NewTaggedArray(length); + for (array_size_t i = 0; i < length; ++i) { + array->Set(thread, i, GetCallArg(msg, i).GetTaggedValue()); + } + return array; +} +} // namespace panda::ecmascript::base \ No newline at end of file diff --git a/ecmascript/base/builtins_base.h b/ecmascript/base/builtins_base.h new file mode 100644 index 0000000000000000000000000000000000000000..3630c29e599f018eebee0495ffa6f90d83ded01a --- /dev/null +++ b/ecmascript/base/builtins_base.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BASE_BUILTINS_BASE_H +#define PANDA_RUNTIME_ECMASCRIPT_BASE_BUILTINS_BASE_H + +#include "ecmascript/base/string_helper.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env_constants-inl.h" +#include "ecmascript/js_symbol.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/runtime_call_id.h" +#include "ecmascript/tagged_array.h" +#include "ecmascript/vmstat/runtime_stat.h" + +namespace panda::ecmascript { +class JSArray; +namespace base { +class BuiltinsBase { +public: + enum ArgsPosition : uint32_t { FIRST = 0, SECOND, THIRD, FOURTH, FIFTH }; + static JSHandle GetArgsArray(EcmaRuntimeCallInfo *msg); + static inline JSHandle GetConstructor(EcmaRuntimeCallInfo *msg) + { + return msg->GetFunction(); + } + + static inline JSHandle GetThis(EcmaRuntimeCallInfo *msg) + { + return msg->GetThis(); + } + + static inline JSHandle GetNewTarget(EcmaRuntimeCallInfo *msg) + { + return msg->GetNewTarget(); + } + + static inline JSHandle GetCallArg(EcmaRuntimeCallInfo *msg, uint32_t position) + { + if (position >= msg->GetArgsNumber()) { + JSThread *thread = msg->GetThread(); + return thread->GlobalConstants()->GetHandledUndefined(); + } + return msg->GetCallArg(position); + } + + static inline JSTaggedValue GetTaggedInt(int32_t value) + { + return JSTaggedValue(value); + } + + static inline JSTaggedValue GetTaggedDouble(double value) + { + return JSTaggedValue(value); + } + + static inline JSTaggedValue GetTaggedBoolean(bool value) + { + return JSTaggedValue(value); + } + + static inline JSTaggedValue GetTaggedString(JSThread *thread, const char *str) + { + return thread->GetEcmaVM()->GetFactory()->NewFromString(str).GetTaggedValue(); + } +}; +} // namespace base +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_H diff --git a/ecmascript/base/error_helper.cpp b/ecmascript/base/error_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d5c8a1b9570186027c665c45ccfceacf7bd9fe5e --- /dev/null +++ b/ecmascript/base/error_helper.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/error_helper.h" +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/base/error_type.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/interpreter/interpreter.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tooling/pt_js_extractor.h" + +namespace panda::ecmascript::base { +using panda::tooling::ecmascript::PtJSExtractor; + +JSTaggedValue ErrorHelper::ErrorCommonToString(EcmaRuntimeCallInfo *argv, const ErrorType &errorType) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be the this value. + // 2. If Type(O) is not Object, throw a TypeError exception + JSHandle thisValue = BuiltinsBase::GetThis(argv); + if (!thisValue->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "ErrorToString:not an object", JSTaggedValue::Exception()); + } + // 3. Let name be Get(O, "name"). + // 4. ReturnIfAbrupt(name). + auto globalConst = thread->GlobalConstants(); + JSHandle handleName = globalConst->GetHandledNameString(); + JSHandle name = JSObject::GetProperty(thread, thisValue, handleName).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If name is undefined, let name be "Error"; otherwise let name be ToString(name). + // 6. ReturnIfAbrupt(name). + name = ErrorHelper::GetErrorName(thread, name, errorType); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. Let msg be Get(O, "message"). + // 8. ReturnIfAbrupt(msg). + JSHandle handleMsg = globalConst->GetHandledMessageString(); + JSHandle msg = JSObject::GetProperty(thread, thisValue, handleMsg).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 9. If msg is undefined, let msg be the empty String; otherwise let msg be ToString(msg). + // 10. ReturnIfAbrupt(msg). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (msg->IsUndefined()) { + msg = JSHandle::Cast(factory->GetEmptyString()); + } else { + msg = JSHandle::Cast(JSTaggedValue::ToString(thread, msg)); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 11. If name is the empty String, return msg. + // 12. If msg is the empty String, return name. + if (JSHandle::Cast(name)->GetLength() == 0) { + return msg.GetTaggedValue(); + } + + if (JSHandle::Cast(msg)->GetLength() == 0) { + return name.GetTaggedValue(); + } + + // 13. Return the result of concatenating name, the code unit 0x003A (COLON), the code unit 0x0020 (SPACE), and msg. + JSHandle space = factory->NewFromString(": "); + JSHandle jsHandleName = JSHandle::Cast(name); + JSHandle jsHandleMsg = JSHandle::Cast(msg); + JSHandle handleNameSpace = factory->ConcatFromString(jsHandleName, space); + JSHandle result = factory->ConcatFromString(handleNameSpace, jsHandleMsg); + return result.GetTaggedValue(); +} + +JSHandle ErrorHelper::GetErrorName(JSThread *thread, const JSHandle &name, + const ErrorType &errorType) +{ + auto globalConst = thread->GlobalConstants(); + if (name->IsUndefined()) { + TaggedObject *errorKey = nullptr; + switch (errorType) { + case ErrorType::RANGE_ERROR: + errorKey = reinterpret_cast(*globalConst->GetHandledRangeErrorString()); + break; + case ErrorType::EVAL_ERROR: + errorKey = reinterpret_cast(*globalConst->GetHandledEvalErrorString()); + break; + case ErrorType::REFERENCE_ERROR: + errorKey = reinterpret_cast(*globalConst->GetHandledReferenceErrorString()); + break; + case ErrorType::TYPE_ERROR: + errorKey = reinterpret_cast(*globalConst->GetHandledTypeErrorString()); + break; + case ErrorType::URI_ERROR: + errorKey = reinterpret_cast(*globalConst->GetHandledURIErrorString()); + break; + case ErrorType::SYNTAX_ERROR: + errorKey = reinterpret_cast(*globalConst->GetHandledSyntaxErrorString()); + break; + default: + errorKey = reinterpret_cast(*globalConst->GetHandledErrorString()); + break; + } + return JSHandle(thread, JSTaggedValue(errorKey)); + } + return JSHandle::Cast(JSTaggedValue::ToString(thread, name)); +} + +JSTaggedValue ErrorHelper::ErrorCommonConstructor(EcmaRuntimeCallInfo *argv, + [[maybe_unused]] const ErrorType &errorType) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle ctor = BuiltinsBase::GetConstructor(argv); + JSMutableHandle newTarget(BuiltinsBase::GetNewTarget(argv)); + if (newTarget->IsUndefined()) { + newTarget.Update(ctor.GetTaggedValue()); + } + JSHandle message = BuiltinsBase::GetCallArg(argv, 0); + + // 2. Let O be OrdinaryCreateFromConstructor(newTarget, "%ErrorPrototype%", «[[ErrorData]]»). + JSHandle nativeInstanceObj = factory->NewJSObjectByConstructor(JSHandle(ctor), newTarget); + + // 3. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. If message is not undefined, then + // a. Let msg be ToString(message). + // b. ReturnIfAbrupt(msg). + // c. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false, + // [[Configurable]]: true}. + // d. Let status be DefinePropertyOrThrow(O, "message", msgDesc). + // e. Assert: status is not an abrupt completion + auto globalConst = thread->GlobalConstants(); + if (!message->IsUndefined()) { + JSHandle handleStr = JSTaggedValue::ToString(thread, message); + LOG(DEBUG, ECMASCRIPT) << "Ark throw error: " << utf::Mutf8AsCString(handleStr->GetDataUtf8()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle msgKey = globalConst->GetHandledMessageString(); + PropertyDescriptor msgDesc(thread, JSHandle::Cast(handleStr), true, false, true); + [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, msgKey, msgDesc); + ASSERT_PRINT(status == true, "return result exception!"); + } + + ASSERT(thread->IsEcmaInterpreter()); + JSHandle handleStack = BuildEcmaStackTrace(thread); + JSHandle stackkey = globalConst->GetHandledStackString(); + PropertyDescriptor stackDesc(thread, JSHandle::Cast(handleStack), true, false, true); + [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, stackkey, stackDesc); + ASSERT_PRINT(status == true, "return result exception!"); + + // 5. Return O. + return nativeInstanceObj.GetTaggedValue(); +} + +CString ErrorHelper::DecodeFunctionName(const char *methodName) +{ + CString name(methodName); + if (name.empty()) { + return "anonymous"; + } + return name; +} + +JSHandle ErrorHelper::BuildEcmaStackTrace(JSThread *thread) +{ + CString data = BuildNativeEcmaStackTrace(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + LOG(DEBUG, ECMASCRIPT) << data; + return factory->NewFromString(data); +} + +CString ErrorHelper::BuildNativeEcmaStackTrace(JSThread *thread) +{ + auto ecmaVm = thread->GetEcmaVM(); + CString data; + EcmaFrameHandler frameHandler(thread); + for (; frameHandler.HasFrame(); frameHandler.PrevFrame()) { + auto method = frameHandler.GetMethod(); + if (method->IsNative()) { + data += INTRINSIC_METHOD_NAME; + } else { + data.append(" at "); + data += DecodeFunctionName( + utf::Mutf8AsCString(method->GetStringDataAnnotation(Method::AnnotationField::FUNCTION_NAME).data)); + data.append(" ("); + // source file + PtJSExtractor *debugExtractor = ecmaVm->GetDebugInfoExtractor(method->GetPandaFile()); + const CString &sourceFile = debugExtractor->GetSourceFile(method->GetFileId()); + if (sourceFile.empty()) { + data.push_back('?'); + } else { + data += sourceFile; + } + data.push_back(':'); + // line number + if (debugExtractor->MatchWithOffset( + [&data](int line) -> bool { + data += ToCString(line + 1); + return true; + }, + method->GetFileId(), frameHandler.GetBytecodeOffset())) { + } else { + data.push_back('?'); + } + data.push_back(')'); + } + data.push_back('\n'); + } + + return data; +} +} // namespace panda::ecmascript::base diff --git a/ecmascript/base/error_helper.h b/ecmascript/base/error_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..d35eec8b092775cdad4676e91c2920bcbd95b99b --- /dev/null +++ b/ecmascript/base/error_helper.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BASE_ERROR_HELPER_H +#define PANDA_RUNTIME_ECMASCRIPT_BASE_ERROR_HELPER_H + +#include "ecmascript/base/error_type.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/mem/c_string.h" + +namespace panda::ecmascript::base { +constexpr char DEFAULT_EMPTY_STACK_TRACE[] = "stack is empty"; // NOLINT (modernize-avoid-c-arrays) +constexpr char INTRINSIC_METHOD_NAME[] = "Intrinsic method"; // NOLINT (modernize-avoid-c-arrays) + +class ErrorHelper { +public: + static JSTaggedValue ErrorCommonToString(EcmaRuntimeCallInfo *argv, const ErrorType &errorType); + + static JSTaggedValue ErrorCommonConstructor(EcmaRuntimeCallInfo *argv, const ErrorType &errorType); + + static CString BuildNativeEcmaStackTrace(JSThread *thread); + +private: + static CString DecodeFunctionName(const char *methodName); + static JSHandle BuildEcmaStackTrace(JSThread *thread); + + static JSHandle GetErrorName(JSThread *thread, const JSHandle &name, + const ErrorType &errorType); +}; +} // namespace panda::ecmascript::base + +#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_ERROR_HELPER_H diff --git a/ecmascript/base/error_type.h b/ecmascript/base/error_type.h new file mode 100644 index 0000000000000000000000000000000000000000..998856740f82d3094aa354fa7a2397a4f884812f --- /dev/null +++ b/ecmascript/base/error_type.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BASE_ERROR_TYPE_H +#define PANDA_RUNTIME_ECMASCRIPT_BASE_ERROR_TYPE_H + +#include + +namespace panda::ecmascript::base { +enum class ErrorType : uint8_t { + ERROR = 0, + EVAL_ERROR, + RANGE_ERROR, + REFERENCE_ERROR, + SYNTAX_ERROR, + TYPE_ERROR, + URI_ERROR, +}; +} // namespace panda::ecmascript::base + +#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_ERROR_TYPE_H diff --git a/ecmascript/base/json_parser.cpp b/ecmascript/base/json_parser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a4acd07a5b199e7f7ead8ce762fc6f5a1249ba47 --- /dev/null +++ b/ecmascript/base/json_parser.cpp @@ -0,0 +1,644 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/json_parser.h" +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/base/number_helper.h" +#include "ecmascript/base/string_helper.h" +#include "ecmascript/base/utf_helper.h" +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript::base { +constexpr unsigned int UNICODE_DIGIT_LENGTH = 4; +constexpr unsigned int NUMBER_TEN = 10; +constexpr unsigned int NUMBER_SIXTEEN = 16; + +constexpr unsigned char CODE_SPACE = 0x20; +JSHandle JsonParser::Parse(Text begin, Text end) +{ + end_ = end - 1; + current_ = begin; + + auto vm = thread_->GetEcmaVM(); + factory_ = vm->GetFactory(); + env_ = *vm->GetGlobalEnv(); + + SkipEndWhiteSpace(); + range_ = end_; + JSTaggedValue result = ParseJSONText(); + return JSHandle(thread_, result); +} + +JSHandle JsonParser::Parse(EcmaString *str) +{ + ASSERT(str != nullptr); + if (UNLIKELY(str->IsUtf16())) { + size_t len = base::utf_helper::Utf16ToUtf8Size(str->GetDataUtf16(), str->GetLength()) - 1; + CVector buf(len); + len = base::utf_helper::ConvertRegionUtf16ToUtf8(str->GetDataUtf16(), buf.data(), str->GetLength(), len, 0); + Text begin = buf.data(); + return Parse(begin, begin + len); + } + + CVector buf(str->GetUtf8Length()); + str->CopyDataUtf8(buf.data(), str->GetUtf8Length()); + Text begin = buf.data(); + return Parse(begin, begin + str->GetLength()); +} + +template +JSTaggedValue JsonParser::ParseJSONText() +{ + SkipStartWhiteSpace(); + Tokens token = ParseToken(); + switch (token) { + case Tokens::OBJECT: + return ParseObject(); + case Tokens::ARRAY: + return ParseArray(); + case Tokens::LITERAL_TRUE: + return ParseLiteral("true", Tokens::LITERAL_TRUE); + case Tokens::LITERAL_FALSE: + return ParseLiteral("false", Tokens::LITERAL_FALSE); + case Tokens::LITERAL_NULL: + return ParseLiteral("null", Tokens::LITERAL_NULL); + case Tokens::NUMBER: + return ParseNumber(); + case Tokens::STRING: + return ParseString(); + default: + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } +} + +JsonParser::Tokens JsonParser::ParseToken() +{ + switch (*current_) { + case '{': + return Tokens::OBJECT; + case '[': + return Tokens::ARRAY; + case '"': + return Tokens::STRING; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + return Tokens::NUMBER; + case 't': + return Tokens::LITERAL_TRUE; + case 'f': + return Tokens::LITERAL_FALSE; + case 'n': + return Tokens::LITERAL_NULL; + default: + return Tokens::TOKEN_ILLEGAL; + } +} + + +void JsonParser::SkipEndWhiteSpace() +{ + while (current_ != end_) { + if (*end_ == ' ' || *end_ == '\r' || *end_ == '\n' || *end_ == '\t') { + end_--; + } else { + break; + } + } +} + +void JsonParser::SkipStartWhiteSpace() +{ + while (current_ != end_) { + if (*current_ == ' ' || *current_ == '\r' || *current_ == '\n' || *current_ == '\t') { + current_++; + } else { + break; + } + } +} + +JSTaggedValue JsonParser::ParseLiteral(CString str, Tokens literalToken) +{ + uint32_t strLen = str.size() - 1; + if (UNLIKELY(range_ - current_ < strLen)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } + + bool isMatch = MatchText(str, strLen); + if (LIKELY(isMatch)) { + switch (literalToken) { + case Tokens::LITERAL_TRUE: + return JSTaggedValue::True(); + case Tokens::LITERAL_FALSE: + return JSTaggedValue::False(); + case Tokens::LITERAL_NULL: + return JSTaggedValue::Null(); + default: + UNREACHABLE(); + } + } + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); +} + +bool JsonParser::MatchText(CString str, uint32_t matchLen) +{ + const char *text = str.c_str(); + uint32_t pos = 1; + while (pos <= matchLen) { + if (current_[pos] != text[pos]) { + return false; + } + pos++; + } + current_ += matchLen; + return true; +} + +template +JSTaggedValue JsonParser::ParseNumber() +{ + if (inObjOrArr) { + bool isNumber = ReadNumberRange(); + if (!isNumber) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + } + + Text current = current_; + bool hasExponent = false; + if (*current_ == '-') { + if (UNLIKELY(current_++ == end_)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + } + if (*current_ == '0') { + if (current_++ != end_) { + if (*current_ == '.') { + if (!IsDecimalsLegal(hasExponent)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + } else if (*current_ == 'e' || *current_ == 'E') { + if (!IsExponentLegal(hasExponent)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + } + } else if (*current_ >= '1' && *current_ <= '9') { + while (current_ != end_) { + current_++; + if (IsNumberCharacter(*current_)) { + continue; + } else if (*current_ == '.') { + if (!IsDecimalsLegal(hasExponent)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + } else if (*current_ == 'e' || *current_ == 'E') { + if (!IsExponentLegal(hasExponent)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + } + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + + double result = NumberHelper::StringToDouble(current, end_ + 1, 0, 0); + current_ = end_; + return JSTaggedValue(result); +} + +bool JsonParser::ReadNumberRange() +{ + Text current = current_; + while (current != range_) { + if (!(IsNumberCharacter(*current)) && !IsNumberSignalCharacter(*current)) { + Text end = current; + while (current != range_) { + if (*current == ' ' || *current == '\r' || *current == '\n' || *current == '\t') { + current++; + } else if (*current == ',' || *current == ']' || *current == '}') { + end_ = end - 1; + return true; + } else { + return false; + } + } + return false; + } + end_ = range_ - 1; + current++; + } + return true; +} + +bool JsonParser::IsNumberCharacter(uint8_t ch) +{ + if (ch >= '0' && ch <= '9') { + return true; + } + return false; +} + +bool JsonParser::IsNumberSignalCharacter(uint8_t ch) +{ + return ch == '.' || ch == 'e' || ch == 'E' || ch == '+' || ch == '-'; +} + +bool JsonParser::IsExponentNumber() +{ + if (IsNumberCharacter(*current_)) { + return true; + } else if (*current_ == '-' || *current_ == '+') { + if (current_ == end_) { + return false; + } + current_++; + if (IsNumberCharacter(*current_)) { + return true; + } + } + return false; +} + +bool JsonParser::IsDecimalsLegal(bool &hasExponent) +{ + if (current_ == end_ && !IsNumberCharacter(*++current_)) { + return false; + } + + while (current_ != end_) { + current_++; + if (IsNumberCharacter(*current_)) { + continue; + } else if (*current_ == 'e' || *current_ == 'E') { + if (hasExponent || current_ == end_) { + return false; + } + current_++; + if (!IsExponentNumber()) { + return false; + } + hasExponent = true; + } else { + return false; + } + } + return true; +} + +bool JsonParser::IsExponentLegal(bool &hasExponent) +{ + if (hasExponent || current_ == end_) { + return false; + } + current_++; + if (!IsExponentNumber()) { + return false; + } + while (current_ != end_) { + if (!IsNumberCharacter(*current_)) { + return false; + } + current_++; + } + return true; +} + +bool JsonParser::ReadStringRange(bool &isFast) +{ + uint8_t c = 0; + Text current = current_; + if (current == range_) { + return false; + } + while (current != range_) { + c = *current; + if (c == '"') { + end_ = current; + return true; + } else if (UNLIKELY(c < CODE_SPACE)) { + return false; + } else if (UNLIKELY(c == base::utf_helper::UTF8_2B_FIRST)) { + uint8_t next = *(current + 1); + if (next == base::utf_helper::UTF8_2B_SECOND) { // special case for U+0000 => C0 80 + return false; + } + } else if (c == '\\') { + isFast = false; + } + + current++; + } + return false; +} + +template +JSTaggedValue JsonParser::ParseString() +{ + bool isFast = true; + if (inObjorArr) { + current_++; + bool isString = ReadStringRange(isFast); + if (!isString) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSTaggedValue::Exception()); + } + if (isFast) { + CString value(current_, end_); + current_ = end_; + + return factory_->NewFromString(value).GetTaggedValue(); + } + } else { + if (*end_ != '"' || current_ == end_) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSTaggedValue::Exception()); + } + current_++; + Text current = current_; + while (current != end_) { + if (UNLIKELY(*current < CODE_SPACE)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } + current++; + } + CString value(current_, end_); + isFast = IsFastParseString(value); + if (LIKELY(isFast)) { + return factory_->NewFromString(value).GetTaggedValue(); + } + } + end_--; + CString res; + while (current_ <= end_) { + if (*current_ == '\\') { + if (current_ == end_) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } + current_++; + switch (*current_) { + case '\"': + res += "\""; + break; + case '\\': + res += "\\"; + break; + case '/': + res += "/"; + break; + case 'b': + res += "\b"; + break; + case 'f': + res += "\f"; + break; + case 'n': + res += "\n"; + break; + case 'r': + res += "\r"; + break; + case 't': + res += "\t"; + break; + case 'u': { + CVector vec; + if (UNLIKELY(!ConvertStringUnicode(vec))) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } + std::u16string u16Str; + u16Str.assign(vec.begin(), vec.end()); + res += base::StringHelper::U16stringToString(u16Str); + break; + } + default: + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } + } else { + res += *current_; + } + current_++; + } + return factory_->NewFromString(res).GetTaggedValue(); +} + +bool JsonParser::IsFastParseString(CString &value) +{ + if (strpbrk(value.c_str(), "\"\\/\b\f\n\r\t") != nullptr) { + return false; + } + return true; +} + +bool JsonParser::ConvertStringUnicode(CVector &vec) +{ + if (end_ - current_ < UNICODE_DIGIT_LENGTH) { + return false; + } + uint16_t res = 0; + uint32_t exponent = UNICODE_DIGIT_LENGTH; + for (uint32_t pos = 0; pos < UNICODE_DIGIT_LENGTH; pos++) { + current_++; + exponent--; + if (*current_ >= '0' && *current_ <= '9') { + res += (*current_ - '0') * pow(NUMBER_SIXTEEN, exponent); + } else if (*current_ >= 'a' && *current_ <= 'f') { + res += (*current_ - 'a' + NUMBER_TEN) * pow(NUMBER_SIXTEEN, exponent); + } else if (*current_ >= 'A' && *current_ <= 'F') { + res += (*current_ - 'A' + NUMBER_TEN) * pow(NUMBER_SIXTEEN, exponent); + } else { + return false; + } + } + if (res < CODE_SPACE) { + return false; + } + current_++; + vec.emplace_back(res); + + if (*(current_ + 1) == '\\' && *(current_ + 2) == 'u') { // 2: next two chars + current_ += 2; // 2: point moves backwards by two digits + return ConvertStringUnicode(vec); + } + return true; +} + +template +JSTaggedValue JsonParser::ParseArray() +{ + if (UNLIKELY(*range_ != ']' && !inObjorArr)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception()); + } + + current_++; + JSHandle arr = factory_->NewJSArray(); + if (*current_ == ']') { + return arr.GetTaggedValue(); + } + + JSMutableHandle valueHandle(thread_, JSTaggedValue::Undefined()); + uint32_t index = 0; + while (current_ <= range_) { + valueHandle.Update(ParseJSONText()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + FastRuntimeStub::SetPropertyByIndex(thread_, arr.GetTaggedValue(), index++, valueHandle.GetTaggedValue()); + current_++; + SkipStartWhiteSpace(); + if (*current_ == ',') { + current_++; + } else if (*current_ == ']') { + if (inObjorArr || current_ == range_) { + return arr.GetTaggedValue(); + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception()); + } + } + } + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception()); +} + +template +JSTaggedValue JsonParser::ParseObject() +{ + if (UNLIKELY(*range_ != '}' && !inObjorArr)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); + } + + JSHandle proto = env_->GetObjectFunction(); + JSHandle result = factory_->NewJSObjectByConstructor(JSHandle(proto), proto); + current_++; + if (*current_ == '}') { + return result.GetTaggedValue(); + } + + JSMutableHandle keyHandle(thread_, JSTaggedValue::Undefined()); + JSMutableHandle valueHandle(thread_, JSTaggedValue::Undefined()); + while (current_ <= range_) { + SkipStartWhiteSpace(); + if (*current_ == '"') { + keyHandle.Update(ParseString()); + } else { + if (*current_ == '}' && (inObjorArr || current_ == range_)) { + return result.GetTaggedValue(); + } + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); + } + current_++; + SkipStartWhiteSpace(); + if (*current_ == ':') { + current_++; + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); + } + valueHandle.Update(ParseJSONText()); + FastRuntimeStub::SetPropertyByValue(thread_, result.GetTaggedValue(), keyHandle.GetTaggedValue(), + valueHandle.GetTaggedValue()); + current_++; + SkipStartWhiteSpace(); + if (*current_ == ',') { + current_++; + } else if (*current_ == '}') { + if (inObjorArr || current_ == range_) { + return result.GetTaggedValue(); + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); + } + } + } + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); +} + +JSHandle JsonParser::InternalizeJsonProperty(const JSHandle &holder, + const JSHandle &name, + const JSHandle &receiver) +{ + JSHandle objHandle(holder); + JSHandle val = JSTaggedValue::GetProperty(thread_, objHandle, name).GetValue(); + JSHandle lengthKey = thread_->GlobalConstants()->GetHandledLengthString(); + if (val->IsECMAObject()) { + JSHandle obj = JSTaggedValue::ToObject(thread_, val); + bool isArray = val->IsArray(thread_); + if (isArray) { + JSHandle lenResult = JSTaggedValue::GetProperty(thread_, val, lengthKey).GetValue(); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenResult); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + array_size_t length = lenNumber.ToUint32(); + JSMutableHandle keyUnknow(thread_, JSTaggedValue::Undefined()); + JSMutableHandle keyName(thread_, JSTaggedValue::Undefined()); + for (array_size_t i = 0; i < length; i++) { + // Let prop be ! ToString((I)). + keyUnknow.Update(JSTaggedValue(i)); + keyName.Update(JSTaggedValue::ToString(thread_, keyUnknow).GetTaggedValue()); + RecurseAndApply(obj, keyName, receiver); + } + } else { + // Let keys be ? EnumerableOwnPropertyNames(val, key). + JSHandle ownerNames(JSObject::EnumerableOwnNames(thread_, obj)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + array_size_t namesLength = ownerNames->GetLength(); + JSMutableHandle keyName(thread_, JSTaggedValue::Undefined()); + for (array_size_t i = 0; i < namesLength; i++) { + keyName.Update(JSTaggedValue::GetProperty(thread_, JSHandle(ownerNames), i) + .GetValue() + .GetTaggedValue()); + RecurseAndApply(obj, keyName, receiver); + } + } + } + + // Return ? Call(receiver, holder, « name, val »). + array_size_t length = 2; + JSHandle msg = factory_->NewTaggedArray(length); + msg->Set(thread_, 0, name); + msg->Set(thread_, 1, val); + JSTaggedValue result = JSFunction::Call(thread_, receiver, objHandle, msg); + return JSHandle(thread_, result); +} + +bool JsonParser::RecurseAndApply(const JSHandle &holder, const JSHandle &name, + const JSHandle &receiver) +{ + JSHandle value = InternalizeJsonProperty(holder, name, receiver); + bool changeResult = false; + + // If newElement is undefined, then Perform ? val.[[Delete]](P). + if (value->IsUndefined()) { + changeResult = JSObject::DeleteProperty(thread_, holder, name); + } else { + // Perform ? CreateDataProperty(val, P, newElement) + changeResult = JSObject::CreateDataProperty(thread_, holder, name, value); + } + return changeResult; +} +} // namespace panda::ecmascript::base diff --git a/ecmascript/base/json_parser.h b/ecmascript/base/json_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..ba8042533566a6644f324a43f12145fd34923ad2 --- /dev/null +++ b/ecmascript/base/json_parser.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BASE_JSON_PARSE_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_BASE_JSON_PARSE_INL_H + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_function.h" + +namespace panda::ecmascript::base { +class JsonParser { +public: + using Text = const uint8_t *; + explicit JsonParser() = default; + explicit JsonParser(JSThread *thread) : thread_(thread) {} + ~JsonParser() = default; + NO_COPY_SEMANTIC(JsonParser); + NO_MOVE_SEMANTIC(JsonParser); + JSHandle Parse(Text begin, Text end); + JSHandle Parse(EcmaString *str); + JSHandle InternalizeJsonProperty(const JSHandle &holder, + const JSHandle &name, + const JSHandle &receiver); + +private: + enum class Tokens : uint8_t { + // six structural tokens + OBJECT = 0, + ARRAY, + NUMBER, + STRING, + LITERAL_TRUE, + LITERAL_FALSE, + LITERAL_NULL, + TOKEN_ILLEGAL, + }; + + template + JSTaggedValue ParseJSONText(); + + template + JSTaggedValue ParseNumber(); + + template + JSTaggedValue ParseString(); + + template + JSTaggedValue ParseArray(); + + template + JSTaggedValue ParseObject(); + + void SkipEndWhiteSpace(); + void SkipStartWhiteSpace(); + JsonParser::Tokens ParseToken(); + JSTaggedValue ParseLiteral(CString str, Tokens literalToken); + bool MatchText(CString str, uint32_t matchLen); + bool ReadNumberRange(); + bool IsNumberCharacter(uint8_t ch); + bool IsNumberSignalCharacter(uint8_t ch); + bool IsExponentNumber(); + bool IsDecimalsLegal(bool &hasExponent); + bool IsExponentLegal(bool &hasExponent); + bool ReadStringRange(bool &isFast); + bool IsFastParseString(CString &value); + bool ConvertStringUnicode(CVector &vec); + + bool RecurseAndApply(const JSHandle &holder, const JSHandle &name, + const JSHandle &receiver); + + Text end_{nullptr}; + Text current_{nullptr}; + Text range_{nullptr}; + JSThread *thread_{nullptr}; + ObjectFactory *factory_{nullptr}; + GlobalEnv *env_{nullptr}; +}; +} // namespace panda::ecmascript::base + +#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_JSON_PARSE_INL_H diff --git a/ecmascript/base/json_stringifier.cpp b/ecmascript/base/json_stringifier.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d07257637304f60f62cb1d6438c574501da3685 --- /dev/null +++ b/ecmascript/base/json_stringifier.cpp @@ -0,0 +1,772 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/json_stringifier.h" +#include +#include +#include +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/base/number_helper.h" +#include "ecmascript/builtins/builtins_errors.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript::base { +constexpr unsigned char CODE_SPACE = 0x20; +constexpr int GAP_MAX_LEN = 10; +constexpr int FOUR_HEX = 4; + +bool JsonStringifier::IsFastValueToQuotedString(const char *value) +{ + if (strpbrk(value, "\"\\\b\f\n\r\t") == nullptr) { + return false; + } + while (*value != '\0') { + if (*value > 0 && *value < CODE_SPACE) { + return false; + } + value++; + } + return true; +} + +CString JsonStringifier::ValueToQuotedString(CString str) +{ + CString product; + const char *value = str.c_str(); + // fast mode + bool isFast = IsFastValueToQuotedString(value); + if (isFast) { + product += "\""; + product += str; + product += "\""; + return product; + } + // 1. Let product be code unit 0x0022 (QUOTATION MARK). + product += "\""; + // 2. For each code unit C in value + for (const char *c = value; *c != 0; ++c) { + switch (*c) { + /* + * a. If C is 0x0022 (QUOTATION MARK) or 0x005C (REVERSE SOLIDUS), then + * i. Let product be the concatenation of product and code unit 0x005C (REVERSE SOLIDUS). + * ii. Let product be the concatenation of product and C. + */ + case '\"': + product += "\\\""; + break; + case '\\': + product += "\\\\"; + break; + /* + * b. Else if C is 0x0008 (BACKSPACE), 0x000C (FORM FEED), 0x000A (LINE FEED), 0x000D (CARRIAGE RETURN), + * or 0x000B (LINE TABULATION), then + * i. Let product be the concatenation of product and code unit 0x005C (REVERSE SOLIDUS). + * ii. Let abbrev be the String value corresponding to the value of C as follows: + * BACKSPACE "b" + * FORM FEED (FF) "f" + * LINE FEED (LF) "n" + * CARRIAGE RETURN (CR) "r" + * LINE TABULATION "t" + * iii. Let product be the concatenation of product and abbrev. + */ + case '\b': + product += "\\b"; + break; + case '\f': + product += "\\f"; + break; + case '\n': + product += "\\n"; + break; + case '\r': + product += "\\r"; + break; + case '\t': + product += "\\t"; + break; + default: + // c. Else if C has a code unit value less than 0x0020 (SPACE), then + if (*c > 0 && *c < CODE_SPACE) { + /* + * i. Let product be the concatenation of product and code unit 0x005C (REVERSE SOLIDUS). + * ii. Let product be the concatenation of product and "u". + * iii. Let hex be the string result of converting the numeric code unit value of C to a String of + * four hexadecimal digits. Alphabetic hexadecimal digits are presented as lowercase Latin letters. + * iv. Let product be the concatenation of product and hex. + */ + std::ostringstream oss; + oss << "\\u" << std::hex << std::setfill('0') << std::setw(FOUR_HEX) << static_cast(*c); + product += oss.str(); + } else { + // Else, + // i. Let product be the concatenation of product and C. + product += *c; + } + } + } + // 3. Let product be the concatenation of product and code unit 0x0022 (QUOTATION MARK). + product += "\""; + // Return product. + return product; +} + +JSHandle JsonStringifier::Stringify(const JSHandle &value, + const JSHandle &replacer, + const JSHandle &gap) +{ + factory_ = thread_->GetEcmaVM()->GetFactory(); + handleValue_ = JSMutableHandle(thread_, JSTaggedValue::Undefined()); + handleKey_ = JSMutableHandle(thread_, JSTaggedValue::Undefined()); + // Let isArray be IsArray(replacer). + bool isArray = replacer->IsArray(thread_); + // ReturnIfAbrupt(isArray). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + // If isArray is true, then + if (isArray) { + uint32_t len; + if (replacer->IsJSArray()) { + // FastPath + JSHandle arr(replacer); + len = arr->GetArrayLength(); + } else { + // Let len be ToLength(Get(replacer, "length")). + JSHandle lengthKey = thread_->GlobalConstants()->GetHandledLengthString(); + JSHandle lenResult = JSTaggedValue::GetProperty(thread_, replacer, lengthKey).GetValue(); + // ReturnIfAbrupt(len). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenResult); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + len = lenNumber.ToUint32(); + } + if (len > 0) { + JSMutableHandle propHandle(thread_, JSTaggedValue::Undefined()); + // Repeat while kGetValue(); + if (primitive.IsNumber() || primitive.IsString()) { + AddDeduplicateProp(propHandle); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + } + } + } + } + } + + // If Type(space) is Object, then + if (gap->IsJSPrimitiveRef()) { + JSTaggedValue primitive = JSPrimitiveRef::Cast(gap->GetTaggedObject())->GetValue(); + // a. If space has a [[NumberData]] internal slot, then + if (primitive.IsNumber()) { + // i. Let space be ToNumber(space). + JSTaggedNumber num = JSTaggedValue::ToNumber(thread_, gap); + // ii. ReturnIfAbrupt(space). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + CalculateNumberGap(num); + } else if (primitive.IsString()) { + // b. Else if space has a [[StringData]] internal slot, then + // i. Let space be ToString(space). + auto str = JSTaggedValue::ToString(thread_, gap); + // ii. ReturnIfAbrupt(space). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + CalculateStringGap(JSHandle(thread_, str.GetTaggedValue())); + } + } else if (gap->IsNumber()) { + // If Type(space) is Number + CalculateNumberGap(gap.GetTaggedValue()); + } else if (gap->IsString()) { + // Else if Type(space) is String + CalculateStringGap(JSHandle::Cast(gap)); + } + + JSHandle key(factory_->GetEmptyString()); + JSTaggedValue serializeValue = GetSerializeValue(value, key, value, replacer); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + handleValue_.Update(serializeValue); + JSTaggedValue result = SerializeJSONProperty(handleValue_, replacer); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + if (!result.IsUndefined()) { + return JSHandle( + factory_->NewFromUtf8Literal(reinterpret_cast(result_.c_str()), result_.size())); + } + return thread_->GlobalConstants()->GetHandledUndefined(); +} + +void JsonStringifier::AddDeduplicateProp(const JSHandle &property) +{ + array_size_t propLen = propList_.size(); + for (array_size_t i = 0; i < propLen; i++) { + if (JSTaggedValue::SameValue(propList_[i], property)) { + return; + } + } + JSHandle primString = JSTaggedValue::ToString(thread_, property); + RETURN_IF_ABRUPT_COMPLETION(thread_); + JSHandle addVal(thread_, *primString); + propList_.emplace_back(addVal); +} + +bool JsonStringifier::CalculateNumberGap(JSTaggedValue gap) +{ + double numValue = gap.GetNumber(); + int num = static_cast(numValue); + if (num > 0) { + int gapLength = std::min(num, GAP_MAX_LEN); + for (int i = 0; i < gapLength; i++) { + gap_ += " "; + } + gap_.append("\0"); + } + return true; +} + +bool JsonStringifier::CalculateStringGap(const JSHandle &primString) +{ + CString gapString = ConvertToString(*primString); + int gapLen = gapString.length(); + if (gapLen > 0) { + int gapLength = std::min(gapLen, GAP_MAX_LEN); + CString str = gapString.substr(0, gapLength); + gap_ += str; + gap_.append("\0"); + } + return true; +} + +JSTaggedValue JsonStringifier::GetSerializeValue(const JSHandle &object, + const JSHandle &key, + const JSHandle &value, + const JSHandle &replacer) +{ + JSTaggedValue tagValue = value.GetTaggedValue(); + // If Type(value) is Object, then + if (value->IsECMAObject()) { + // a. Let toJSON be Get(value, "toJSON"). + JSHandle toJson = thread_->GlobalConstants()->GetHandledToJsonString(); + JSHandle toJsonFun( + thread_, FastRuntimeStub::FastGetPropertyByValue(thread_, tagValue, toJson.GetTaggedValue())); + // b. ReturnIfAbrupt(toJSON). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + + // c. If IsCallable(toJSON) is true + if (UNLIKELY(toJsonFun->IsCallable())) { + array_size_t length = 1; + JSHandle msg = factory_->NewTaggedArray(length); + msg->Set(thread_, 0, key.GetTaggedValue()); + // Let value be Call(toJSON, value, «key»). + tagValue = JSFunction::Call(thread_, toJsonFun, value, msg); + // ii. ReturnIfAbrupt(value). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + } + } + + if (UNLIKELY(replacer->IsCallable())) { + array_size_t length = 2; + handleValue_.Update(tagValue); + JSHandle msg = factory_->NewTaggedArray(length); + msg->Set(thread_, 0, key); + msg->Set(thread_, 1, handleValue_); + // a. Let value be Call(ReplacerFunction, holder, «key, value»). + tagValue = JSFunction::Call(thread_, replacer, object, msg); + // b. ReturnIfAbrupt(value). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + } + return tagValue; +} + +JSTaggedValue JsonStringifier::SerializeJSONProperty(const JSHandle &value, + const JSHandle &replacer) +{ + JSTaggedValue tagValue = value.GetTaggedValue(); + if (!tagValue.IsHeapObject()) { + TaggedType type = tagValue.GetRawData(); + switch (type) { + // If value is false, return "false". + case JSTaggedValue::VALUE_FALSE: + result_ += "false"; + return tagValue; + // If value is true, return "true". + case JSTaggedValue::VALUE_TRUE: + result_ += "true"; + return tagValue; + // If value is null, return "null". + case JSTaggedValue::VALUE_NULL: + result_ += "null"; + return tagValue; + default: + // If Type(value) is Number, then + if (tagValue.IsNumber()) { + // a. If value is finite, return ToString(value). + if (std::isfinite(tagValue.GetNumber())) { + result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, tagValue)); + } else { + // b. Else, return "null". + result_ += "null"; + } + return tagValue; + } + } + } else { + JSType jsType = tagValue.GetTaggedObject()->GetClass()->GetObjectType(); + JSHandle valHandle(thread_, tagValue); + switch (jsType) { + case JSType::JS_ARRAY: { + SerializeJSArray(valHandle, replacer); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + return tagValue; + } + // If Type(value) is String, return QuoteJSONString(value). + case JSType::STRING: { + CString str = ConvertToString(*JSHandle(valHandle)); + str = ValueToQuotedString(str); + result_ += str; + return tagValue; + } + case JSType::JS_PRIMITIVE_REF: { + SerilaizePrimitiveRef(valHandle); + return tagValue; + } + case JSType::SYMBOL: + return JSTaggedValue::Undefined(); + default: { + if (!tagValue.IsCallable()) { + if (UNLIKELY(tagValue.IsJSProxy())) { + SerializeJSProxy(valHandle, replacer); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + } else { + SerializeJSONObject(valHandle, replacer); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + } + return tagValue; + } + } + } + } + return JSTaggedValue::Undefined(); +} + +void JsonStringifier::SerializeObjectKey(const JSHandle &key, bool hasContent) +{ + CString stepBegin; + CString stepEnd; + if (hasContent) { + result_ += ","; + } + if (!gap_.empty()) { + stepBegin += "\n"; + stepBegin += indent_; + stepEnd += " "; + } + CString str; + if (key->IsString()) { + str = ConvertToString(EcmaString::Cast(key->GetTaggedObject())); + } else if (key->IsInt()) { + str = NumberHelper::IntToString(static_cast(key->GetInt())); + } else { + str = ConvertToString(*JSTaggedValue::ToString(thread_, key)); + } + result_ += stepBegin; + str = ValueToQuotedString(str); + result_ += str; + result_ += ":"; + result_ += stepEnd; +} + +bool JsonStringifier::PushValue(const JSHandle &value) +{ + array_size_t thisLen = stack_.size(); + + for (array_size_t i = 0; i < thisLen; i++) { + bool equal = JSTaggedValue::SameValue(stack_[i].GetTaggedValue(), value.GetTaggedValue()); + if (equal) { + return true; + } + } + + stack_.emplace_back(value); + return false; +} + +void JsonStringifier::PopValue() +{ + stack_.pop_back(); +} + +bool JsonStringifier::SerializeJSONObject(const JSHandle &value, const JSHandle &replacer) +{ + bool isContain = PushValue(value); + if (isContain) { + THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true); + } + + CString stepback = indent_; + indent_ += gap_; + + result_ += "{"; + bool hasContent = false; + + JSHandle obj(value); + if (!replacer->IsArray(thread_)) { + uint32_t numOfKeys = obj->GetNumberOfKeys(); + uint32_t numOfElements = obj->GetNumberOfElements(); + if (numOfElements > 0) { + hasContent = JsonStringifier::SerializeElements(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + if (numOfKeys > 0) { + hasContent = JsonStringifier::SerializeKeys(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + } else { + array_size_t propLen = propList_.size(); + for (array_size_t i = 0; i < propLen; i++) { + JSTaggedValue tagVal = + FastRuntimeStub::FastGetPropertyByValue(thread_, obj.GetTaggedValue(), propList_[i].GetTaggedValue()); + handleValue_.Update(tagVal); + JSTaggedValue serializeValue = GetSerializeValue(value, propList_[i], handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() || + (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) { + continue; + } + handleValue_.Update(serializeValue); + SerializeObjectKey(propList_[i], hasContent); + JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (!res.IsUndefined()) { + hasContent = true; + } + } + } + if (hasContent && gap_.length() != 0) { + result_ += "\n"; + result_ += stepback; + } + result_ += "}"; + PopValue(); + indent_ = stepback; + return true; +} + +bool JsonStringifier::SerializeJSProxy(const JSHandle &object, const JSHandle &replacer) +{ + bool isContain = PushValue(object); + if (isContain) { + THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true); + } + + CString stepback = indent_; + CString stepBegin; + indent_ += gap_; + + if (!gap_.empty()) { + stepBegin += "\n"; + stepBegin += indent_; + } + result_ += "["; + JSHandle proxy(object); + JSHandle lengthKey = thread_->GlobalConstants()->GetHandledLengthString(); + JSHandle lenghHandle = JSProxy::GetProperty(thread_, proxy, lengthKey).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenghHandle); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + uint32_t length = lenNumber.ToUint32(); + for (array_size_t i = 0; i < length; i++) { + handleKey_.Update(JSTaggedValue(i)); + JSHandle valHandle = JSProxy::GetProperty(thread_, proxy, handleKey_).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (i > 0) { + result_ += ","; + } + result_ += stepBegin; + JSTaggedValue serializeValue = GetSerializeValue(object, handleKey_, valHandle, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + handleValue_.Update(serializeValue); + JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (res.IsUndefined()) { + result_ += "null"; + } + } + + if (length > 0 && !gap_.empty()) { + result_ += "\n"; + result_ += stepback; + } + result_ += "]"; + PopValue(); + indent_ = stepback; + return true; +} + +bool JsonStringifier::SerializeJSArray(const JSHandle &value, const JSHandle &replacer) +{ + // If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical. + bool isContain = PushValue(value); + if (isContain) { + THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true); + } + + CString stepback = indent_; + CString stepBegin; + indent_ += gap_; + + if (!gap_.empty()) { + stepBegin += "\n"; + stepBegin += indent_; + } + result_ += "["; + JSHandle jsArr(value); + uint32_t len = jsArr->GetArrayLength(); + if (len > 0) { + for (array_size_t i = 0; i < len; i++) { + JSTaggedValue tagVal = FastRuntimeStub::FastGetPropertyByIndex(thread_, value.GetTaggedValue(), i); + if (UNLIKELY(tagVal.IsAccessor())) { + tagVal = JSObject::CallGetter(thread_, AccessorData::Cast(tagVal.GetTaggedObject()), value); + } + handleKey_.Update(JSTaggedValue(i)); + handleValue_.Update(tagVal); + + if (i > 0) { + result_ += ","; + } + result_ += stepBegin; + JSTaggedValue serializeValue = GetSerializeValue(value, handleKey_, handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + handleValue_.Update(serializeValue); + JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (res.IsUndefined()) { + result_ += "null"; + } + } + + if (!gap_.empty()) { + result_ += "\n"; + result_ += stepback; + } + } + + result_ += "]"; + PopValue(); + indent_ = stepback; + return true; +} + +void JsonStringifier::SerilaizePrimitiveRef(const JSHandle &primitiveRef) +{ + JSTaggedValue primitive = JSPrimitiveRef::Cast(primitiveRef.GetTaggedValue().GetTaggedObject())->GetValue(); + if (primitive.IsString()) { + auto priStr = JSTaggedValue::ToString(thread_, primitiveRef); + RETURN_IF_ABRUPT_COMPLETION(thread_); + CString str = ConvertToString(*priStr); + str = ValueToQuotedString(str); + result_ += str; + } else if (primitive.IsNumber()) { + auto priNum = JSTaggedValue::ToNumber(thread_, primitiveRef); + RETURN_IF_ABRUPT_COMPLETION(thread_); + if (std::isfinite(priNum.GetNumber())) { + result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, priNum)); + } else { + result_ += "null"; + } + } else if (primitive.IsBoolean()) { + result_ += primitive.IsTrue() ? "true" : "false"; + } +} + +bool JsonStringifier::SerializeElements(const JSHandle &obj, const JSHandle &replacer, + bool hasContent) +{ + JSHandle elementsArr(thread_, obj->GetElements()); + if (!elementsArr->IsDictionaryMode()) { + uint32_t elementsLen = elementsArr->GetLength(); + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!elementsArr->Get(i).IsHole()) { + handleKey_.Update(JSTaggedValue(i)); + handleValue_.Update(elementsArr->Get(i)); + hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + } + } else { + JSHandle numberDic(elementsArr); + CVector sortArr; + int size = numberDic->Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = numberDic->GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyAttributes attr = numberDic->GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + sortArr.emplace_back(JSTaggedValue(static_cast(key.GetInt()))); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), NumberDictionary::CompKey); + for (const auto &entry : sortArr) { + handleKey_.Update(entry); + int index = numberDic->FindEntry(entry); + JSTaggedValue value = numberDic->GetValue(index); + handleValue_.Update(value); + hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + } + return hasContent; +} + +bool JsonStringifier::SerializeKeys(const JSHandle &obj, const JSHandle &replacer, + bool hasContent) +{ + JSHandle propertiesArr(thread_, obj->GetProperties()); + if (!propertiesArr->IsDictionaryMode()) { + JSHandle jsHclass(thread_, obj->GetJSHClass()); + JSTaggedValue enumCache = jsHclass->GetEnumCache(); + if (!enumCache.IsNull()) { + int propsNumber = jsHclass->GetPropertiesNumber(); + JSHandle cache(thread_, enumCache); + uint32_t length = cache->GetLength(); + for (uint32_t i = 0; i < length; i++) { + JSTaggedValue key = cache->Get(i); + if (!key.IsString()) { + continue; + } + handleKey_.Update(key); + JSTaggedValue value; + LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetAttributes().GetTaggedObject()); + int index = layoutInfo->FindElementWithCache(thread_, *jsHclass, key, propsNumber); + PropertyAttributes attr(layoutInfo->GetAttr(index)); + ASSERT(static_cast(attr.GetOffset()) == index); + value = attr.IsInlinedProps() + ? obj->GetPropertyInlinedProps(index) + : propertiesArr->Get(index - JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + if (UNLIKELY(value.IsAccessor())) { + value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()), + JSHandle(obj)); + } + handleValue_.Update(value); + hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + return hasContent; + } + int end = jsHclass->GetPropertiesNumber(); + if (end <= 0) { + return hasContent; + } + int propsNumber = jsHclass->GetPropertiesNumber(); + for (int i = 0; i < end; i++) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetAttributes().GetTaggedObject()); + JSTaggedValue key = layoutInfo->GetKey(i); + if (key.IsString() && layoutInfo->GetAttr(i).IsEnumerable()) { + handleKey_.Update(key); + JSTaggedValue value; + int index = layoutInfo->FindElementWithCache(thread_, *jsHclass, key, propsNumber); + PropertyAttributes attr(layoutInfo->GetAttr(index)); + ASSERT(static_cast(attr.GetOffset()) == index); + value = attr.IsInlinedProps() + ? obj->GetPropertyInlinedProps(index) + : propertiesArr->Get(index - JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + if (UNLIKELY(value.IsAccessor())) { + value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()), + JSHandle(obj)); + } + handleValue_.Update(value); + hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + } + return hasContent; + } + JSHandle nameDic(propertiesArr); + int size = nameDic->Size(); + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = nameDic->GetKey(hashIndex); + if (!key.IsString()) { + continue; + } + PropertyAttributes attr = nameDic->GetAttributes(hashIndex); + if (!attr.IsEnumerable()) { + continue; + } + std::pair pair(key, attr); + sortArr.emplace_back(pair); + } + std::sort(sortArr.begin(), sortArr.end(), NameDictionary::CompKey); + for (const auto &entry : sortArr) { + handleKey_.Update(entry.first); + int index = nameDic->FindEntry(entry.first); + JSTaggedValue value = nameDic->GetValue(index); + if (UNLIKELY(value.IsAccessor())) { + value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()), + JSHandle(obj)); + } + handleValue_.Update(value); + hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + return hasContent; +} + +bool JsonStringifier::AppendJsonString(const JSHandle &obj, const JSHandle &replacer, + bool hasContent) +{ + JSTaggedValue serializeValue = GetSerializeValue(JSHandle(obj), handleKey_, handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() || + (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) { + return hasContent; + } + handleValue_.Update(serializeValue); + SerializeObjectKey(handleKey_, hasContent); + JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (!res.IsUndefined()) { + return true; + } + return hasContent; +} +} // namespace panda::ecmascript::base diff --git a/ecmascript/base/json_stringifier.h b/ecmascript/base/json_stringifier.h new file mode 100644 index 0000000000000000000000000000000000000000..8aa0ba14c64cbf24ee47006b707e3b74eff9bc8a --- /dev/null +++ b/ecmascript/base/json_stringifier.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BASE_JSON_STRINGIFY_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_BASE_JSON_STRINGIFY_INL_H + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/global_env.h" +#include "ecmascript/mem/c_containers.h" + +namespace panda::ecmascript::base { +class JsonStringifier { +public: + explicit JsonStringifier() = default; + + explicit JsonStringifier(JSThread *thread) : thread_(thread) {} + + ~JsonStringifier() = default; + NO_COPY_SEMANTIC(JsonStringifier); + NO_MOVE_SEMANTIC(JsonStringifier); + + JSHandle Stringify(const JSHandle &value, const JSHandle &replacer, + const JSHandle &gap); + +private: + CString ValueToQuotedString(CString str); + + bool IsFastValueToQuotedString(const char *value); + + void AddDeduplicateProp(const JSHandle &property); + + JSTaggedValue SerializeJSONProperty(const JSHandle &value, const JSHandle &replacer); + JSTaggedValue GetSerializeValue(const JSHandle &object, const JSHandle &key, + const JSHandle &value, const JSHandle &replacer); + void SerializeObjectKey(const JSHandle &key, bool hasContent); + + bool SerializeJSONObject(const JSHandle &value, const JSHandle &replacer); + + bool SerializeJSArray(const JSHandle &value, const JSHandle &replacer); + bool SerializeJSProxy(const JSHandle &object, const JSHandle &replacer); + + void SerilaizePrimitiveRef(const JSHandle &primitiveRef); + + bool PushValue(const JSHandle &value); + + void PopValue(); + + bool CalculateNumberGap(JSTaggedValue gap); + + bool CalculateStringGap(const JSHandle &primString); + bool AppendJsonString(const JSHandle &obj, const JSHandle &replacer, bool hasContent); + bool SerializeElements(const JSHandle &obj, const JSHandle &replacer, bool hasContent); + bool SerializeKeys(const JSHandle &obj, const JSHandle &replacer, bool hasContent); + + static void FastStorePropertyByIndex(JSThread *thread, JSTaggedValue obj, uint32_t idx, JSTaggedValue value); + + CString gap_; + CString result_; + CString indent_; + JSThread *thread_{nullptr}; + ObjectFactory *factory_{nullptr}; + CVector> stack_; + CVector> propList_; + JSMutableHandle handleKey_{}; + JSMutableHandle handleValue_{}; +}; +} // namespace panda::ecmascript::base +#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_JSON_STRINGIFY_INL_H diff --git a/ecmascript/base/number_helper.cpp b/ecmascript/base/number_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..793024c4026c38cd121b75a3737286c2e05965fa --- /dev/null +++ b/ecmascript/base/number_helper.cpp @@ -0,0 +1,684 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/number_helper.h" +#include +#include +#include +#include +#include +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript::base { +enum class Sign { NONE, NEG, POS }; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_IF_CONVERSION_END(p, end, result) \ + if ((p) == (end)) { \ + return (result); \ + } + +constexpr char CHARS[] = "0123456789abcdefghijklmnopqrstuvwxyz"; // NOLINT (modernize-avoid-c-arrays) +constexpr uint64_t MAX_MANTISSA = 0x1ULL << 52U; + +static inline uint8_t ToDigit(uint8_t c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'A' && c <= 'Z') { + return c - 'A' + DECIMAL; + } + if (c >= 'a' && c <= 'z') { + return c - 'a' + DECIMAL; + } + return '$'; +} + +bool NumberHelper::IsNonspace(uint16_t c) +{ + int i; + int len = sizeof(SPACE_OR_LINE_TERMINAL) / sizeof(SPACE_OR_LINE_TERMINAL[0]); + for (i = 0; i < len; i++) { + if (c == SPACE_OR_LINE_TERMINAL[i]) { + return false; + } + if (c < SPACE_OR_LINE_TERMINAL[i]) { + return true; + } + } + return true; +} + +bool NumberHelper::GotoNonspace(uint8_t **ptr, const uint8_t *end) +{ + while (*ptr < end) { + uint16_t c = **ptr; + size_t size = 1; + if (**ptr > INT8_MAX) { + size = 0; + uint16_t utf8Bit = INT8_MAX + 1; // equal 0b1000'0000 + while (utf8Bit > 0 && (c & utf8Bit) == utf8Bit) { + ++size; + utf8Bit >>= 1UL; + } + if (base::utf_helper::ConvertRegionUtf8ToUtf16(*ptr, &c, 1, 0) <= 0) { + return true; + } + } + if (IsNonspace(c)) { + return true; + } + *ptr += size; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + return false; +} + +static inline double SignedZero(Sign sign) +{ + return sign == Sign::NEG ? -0.0 : 0.0; +} + +bool NumberHelper::IsEmptyString(const uint8_t *start, const uint8_t *end) +{ + auto p = const_cast(start); + return !NumberHelper::GotoNonspace(&p, end); +} + +JSTaggedValue NumberHelper::DoubleToString(JSThread *thread, double number, int radix) +{ + bool negative = false; + if (number < 0.0) { + negative = true; + number = -number; + } + + double numberInteger = std::floor(number); + double numberFraction = number - numberInteger; + + auto value = bit_cast(number); + value += 1; + double delta = HALF * (bit_cast(value) - number); + + CString result; + if (numberFraction != 0 && numberFraction >= delta) { + result += "."; + result += DecimalsToString(&numberInteger, numberFraction, radix, delta); + } + + result = IntergerToString(numberInteger, radix) + result; + + if (negative) { + result = "-" + result; + } + + return BuiltinsBase::GetTaggedString(thread, result.c_str()); +} + +JSTaggedValue NumberHelper::DoubleToExponential(JSThread *thread, double number, int digit) +{ + CStringStream ss; + if (digit < 0) { + ss << std::setiosflags(std::ios::scientific) << std::setprecision(base::MAX_PRECISION) << number; + } else { + ss << std::setiosflags(std::ios::scientific) << std::setprecision(digit) << number; + } + CString result = ss.str(); + size_t found = result.find_last_of('e'); + if (found != CString::npos && found < result.size() - 2 && result[found + 2] == '0') { + result.erase(found + 2, 1); // 2:offset of e + } + if (digit < 0) { + size_t end = found; + while (--found > 0) { + if (result[found] != '0') { + break; + } + } + if (result[found] == '.') { + found--; + } + if (found < end - 1) { + result.erase(found + 1, end - found - 1); + } + } + return BuiltinsBase::GetTaggedString(thread, result.c_str()); +} + +JSTaggedValue NumberHelper::DoubleToFixed(JSThread *thread, double number, int digit) +{ + CStringStream ss; + ss << std::setiosflags(std::ios::fixed) << std::setprecision(digit) << number; + return BuiltinsBase::GetTaggedString(thread, ss.str().c_str()); +} + +JSTaggedValue NumberHelper::DoubleToPrecision(JSThread *thread, double number, int digit) +{ + if (number == 0.0) { + return DoubleToFixed(thread, number, digit - 1); + } + CStringStream ss; + double positiveNumber = number > 0 ? number : -number; + int logDigit = std::floor(log10(positiveNumber)); + int radixDigit = digit - logDigit - 1; + const int MAX_EXPONENT_DIGIT = 6; + if ((logDigit >= 0 && radixDigit >= 0) || (logDigit < 0 && radixDigit <= MAX_EXPONENT_DIGIT)) { + return DoubleToFixed(thread, number, std::abs(radixDigit)); + } + return DoubleToExponential(thread, number, digit - 1); +} + +JSTaggedValue NumberHelper::StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix) +{ + auto p = const_cast(start); + JSTaggedValue nanResult = BuiltinsBase::GetTaggedDouble(NAN_VALUE); + // 1. skip space and line terminal + if (!NumberHelper::GotoNonspace(&p, end)) { + return nanResult; + } + + // 2. sign bit + bool negative = false; + if (*p == '-') { + negative = true; + RETURN_IF_CONVERSION_END(++p, end, nanResult); + } else if (*p == '+') { + RETURN_IF_CONVERSION_END(++p, end, nanResult); + } + // 3. 0x or 0X + bool stripPrefix = true; + // 4. If R  0, then + // a. If R < 2 or R > 36, return NaN. + // b. If R  16, let stripPrefix be false. + if (radix != 0) { + if (radix < MIN_RADIX || radix > MAX_RADIX) { + return nanResult; + } + if (radix != HEXADECIMAL) { + stripPrefix = false; + } + } else { + radix = DECIMAL; + } + int size = 0; + if (stripPrefix) { + if (*p == '0') { + size++; + if (++p != end && (*p == 'x' || *p == 'X')) { + RETURN_IF_CONVERSION_END(++p, end, nanResult); + radix = HEXADECIMAL; + } + } + } + + double result = 0; + bool isDone = false; + do { + double part = 0; + uint32_t multiplier = 1; + for (; p != end; ++p) { + // The maximum value to ensure that uint32_t will not overflow + const uint32_t MAX_MULTIPER = 0xffffffffU / 36; + uint32_t m = multiplier * radix; + if (m > MAX_MULTIPER) { + break; + } + + int currentBit = ToDigit(*p); + if (currentBit >= radix) { + isDone = true; + break; + } + size++; + part = part * radix + currentBit; + multiplier = m; + } + result = result * multiplier + part; + if (isDone) { + break; + } + } while (p != end); + + if (size == 0) { + return nanResult; + } + + if (negative) { + result = -result; + } + return BuiltinsBase::GetTaggedDouble(result); +} + +char NumberHelper::Carry(char current, int radix) +{ + int digit = (current > '9') ? (current - 'a' + DECIMAL) : (current - '0'); + digit = (digit == (radix - 1)) ? 0 : digit + 1; + return CHARS[digit]; +} + +CString NumberHelper::IntergerToString(double number, int radix) +{ + ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX); + CString result; + while (number / radix > MAX_MANTISSA) { + number /= radix; + result = CString("0").append(result); + } + do { + double remainder = std::fmod(number, radix); + result = CHARS[static_cast(remainder)] + result; + number = (number - remainder) / radix; + } while (number > 0); + return result; +} + +CString NumberHelper::DecimalsToString(double *numberInteger, double fraction, int radix, double delta) +{ + CString result; + while (fraction >= delta) { + fraction *= radix; + delta *= radix; + int64_t integer = std::floor(fraction); + fraction -= integer; + result += CHARS[integer]; + if (fraction > HALF && fraction + delta > 1) { + size_t fractionEnd = result.size() - 1; + result[fractionEnd] = Carry(*result.rbegin(), radix); + for (; fractionEnd > 0; fractionEnd--) { + if (result[fractionEnd] == '0') { + result[fractionEnd - 1] = Carry(result[fractionEnd - 1], radix); + } else { + break; + } + } + if (fractionEnd == 0) { + (*numberInteger)++; + } + break; + } + } + // delete 0 in the end + size_t found = result.find_last_not_of('0'); + if (found != CString::npos) { + result.erase(found + 1); + } + + return result; +} + +CString NumberHelper::IntToString(int number) +{ + return ToCString(number); +} + +// 7.1.12.1 ToString Applied to the Number Type +JSHandle NumberHelper::NumberToString(const JSThread *thread, JSTaggedValue number) +{ + ASSERT(number.IsNumber()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (number.IsInt()) { + return factory->NewFromString(IntToString(number.GetInt())); + } + + double d = number.GetDouble(); + if (std::isnan(d)) { + return factory->NewFromString("NaN"); + } + if (d == 0.0) { + return factory->NewFromString("0"); + } + if (d >= INT32_MIN + 1 && d <= INT32_MAX && d == static_cast(static_cast(d))) { + return factory->NewFromString(IntToString(static_cast(d))); + } + + std::string result; + if (d < 0) { + result += "-"; + d = -d; + } + + if (std::isinf(d)) { + result += "Infinity"; + return factory->NewFromStdString(result); + } + + ASSERT(d > 0); + + // 5. Otherwise, let n, k, and s be integers such that k ≥ 1, 10k−1 ≤ s < 10k, the Number value for s × 10n−k is m, + // and k is as small as possible. If there are multiple possibilities for s, choose the value of s for which s × + // 10n−k is closest in value to m. If there are two such possible values of s, choose the one that is even. Note + // that k is the number of digits in the decimal representation of s and that s is not divisible by 10. + CStringStream str; + str << std::scientific << std::showpoint << std::setprecision(DOUBLE_MAX_PRECISION) << d; + if (strtod(str.str().c_str(), nullptr) != d) { + str.clear(); + str.str(""); + str << std::scientific << std::showpoint << std::setprecision(DOUBLE_MAX_PRECISION + 1) << d; + } + std::string scientificStr(str.str()); + + auto indexOfE = scientificStr.find_last_of('e'); + ASSERT(indexOfE != std::string::npos); + std::string base = scientificStr.substr(0, indexOfE); + // skip trailing zeros, and base must not be empty. + base = base.substr(0, base.find_last_not_of('0') + 1); + int k = static_cast(base.size()) - 1; + int n = std::stoi(scientificStr.substr(indexOfE + 1)) + 1; + if (n > 0 && n <= 21) { // NOLINT(readability-magic-numbers) + base.erase(1, 1); + if (k <= n) { + // 6. If k ≤ n ≤ 21, return the String consisting of the code units of the k digits of the decimal + // representation of s (in order, with no leading zeroes), followed by n−k occurrences of the code unit + // 0x0030 (DIGIT ZERO). + base += std::string(n - k, '0'); + } else { + // 7. If 0 < n ≤ 21, return the String consisting of the code units of the most significant n digits of the + // decimal representation of s, followed by the code unit 0x002E (FULL STOP), followed by the code units of + // the remaining k−n digits of the decimal representation of s. + base.insert(n, 1, '.'); + } + } else if (-6 < n && n <= 0) { // NOLINT(readability-magic-numbers) + // 8. If −6 < n ≤ 0, return the String consisting of the code unit 0x0030 (DIGIT ZERO), followed by the code + // unit 0x002E (FULL STOP), followed by −n occurrences of the code unit 0x0030 (DIGIT ZERO), followed by the + // code units of the k digits of the decimal representation of s. + base.erase(1, 1); + base = std::string("0.") + std::string(-n, '0') + base; + } else { + if (k == 1) { + // 9. Otherwise, if k = 1, return the String consisting of the code unit of the single digit of s + base.erase(1, 1); + } + // followed by code unit 0x0065 (LATIN SMALL LETTER E), followed by the code unit 0x002B (PLUS SIGN) or the code + // unit 0x002D (HYPHEN-MINUS) according to whether n−1 is positive or negative, followed by the code units of + // the decimal representation of the integer abs(n−1) (with no leading zeroes). + base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1); + } + result += base; + return factory->NewFromStdString(result); +} + +double NumberHelper::TruncateDouble(double d) +{ + if (std::isnan(d)) { + return 0; + } + if (!std::isfinite(d)) { + return d; + } + // -0 to +0 + if (d == 0.0) { + return 0; + } + return (d >= 0) ? std::floor(d) : std::ceil(d); +} + +double NumberHelper::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags) +{ + auto p = const_cast(start); + // 1. skip space and line terminal + if (!NumberHelper::GotoNonspace(&p, end)) { + return 0.0; + } + + // 2. get number sign + Sign sign = Sign::NONE; + if (*p == '+') { + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + sign = Sign::POS; + } else if (*p == '-') { + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + sign = Sign::NEG; + } + bool ignoreTrailing = (flags & IGNORE_TRAILING) != 0; + + // 3. judge Infinity + static const char INF[] = "Infinity"; // NOLINT(modernize-avoid-c-arrays) + if (*p == INF[0]) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for (const char *i = &INF[1]; *i != '\0'; ++i) { + if (++p == end || *p != *i) { + return NAN_VALUE; + } + } + ++p; + if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) { + return NAN_VALUE; + } + return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY; + } + + // 4. get number radix + bool leadingZero = false; + bool prefixRadix = false; + if (*p == '0' && radix == 0) { + RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign)); + if (*p == 'x' || *p == 'X') { + if ((flags & ALLOW_HEX) == 0) { + return ignoreTrailing ? SignedZero(sign) : NAN_VALUE; + } + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + if (sign != Sign::NONE) { + return NAN_VALUE; + } + prefixRadix = true; + radix = HEXADECIMAL; + } else if (*p == 'o' || *p == 'O') { + if ((flags & ALLOW_OCTAL) == 0) { + return ignoreTrailing ? SignedZero(sign) : NAN_VALUE; + } + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + if (sign != Sign::NONE) { + return NAN_VALUE; + } + prefixRadix = true; + radix = OCTAL; + } else if (*p == 'b' || *p == 'B') { + if ((flags & ALLOW_BINARY) == 0) { + return ignoreTrailing ? SignedZero(sign) : NAN_VALUE; + } + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + if (sign != Sign::NONE) { + return NAN_VALUE; + } + prefixRadix = true; + radix = BINARY; + } else { + leadingZero = true; + } + } + + if (radix == 0) { + radix = DECIMAL; + } + auto pStart = p; + // 5. skip leading '0' + while (*p == '0') { + RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign)); + leadingZero = true; + } + // 6. parse to number + uint64_t intNumber = 0; + uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix; + int digits = 0; + int exponent = 0; + do { + uint8_t c = ToDigit(*p); + if (c >= radix) { + if (!prefixRadix || ignoreTrailing || (pStart != p && !NumberHelper::GotoNonspace(&p, end))) { + break; + } + // "0b" "0x1.2" "0b1e2" ... + return NAN_VALUE; + } + ++digits; + if (intNumber < numberMax) { + intNumber = intNumber * radix + c; + } else { + ++exponent; + } + } while (++p != end); + + auto number = static_cast(intNumber); + if (sign == Sign::NEG) { + if (number == 0) { + number = -0.0; + } else { + number = -number; + } + } + + // 7. deal with other radix except DECIMAL + if (p == end || radix != DECIMAL) { + if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && NumberHelper::GotoNonspace(&p, end))) { + // no digits there, like "0x", "0xh", or error trailing of "0x3q" + return NAN_VALUE; + } + return number * std::pow(radix, exponent); + } + + // 8. parse '.' + if (radix == DECIMAL && *p == '.') { + RETURN_IF_CONVERSION_END(++p, end, (digits > 0) ? (number * std::pow(radix, exponent)) : NAN_VALUE); + while (ToDigit(*p) < radix) { + --exponent; + ++digits; + if (++p == end) { + break; + } + } + } + if (digits == 0 && !leadingZero) { + // no digits there, like ".", "sss", or ".e1" + return NAN_VALUE; + } + auto pEnd = p; + + // 9. parse 'e/E' with '+/-' + char exponentSign = '+'; + int additionalExponent = 0; + constexpr int MAX_EXPONENT = INT32_MAX / 2; + if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) { + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + + // 10. parse exponent number + if (*p == '+' || *p == '-') { + exponentSign = static_cast(*p); + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + } + uint8_t digit; + while ((digit = ToDigit(*p)) < radix) { + if (additionalExponent > MAX_EXPONENT / radix) { + additionalExponent = MAX_EXPONENT; + } else { + additionalExponent = additionalExponent * radix + digit; + } + if (++p == end) { + break; + } + } + } + exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent); + if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) { + return NAN_VALUE; + } + + // 10. build StringNumericLiteral string + CString buffer; + if (sign == Sign::NEG) { + buffer += "-"; + } + for (uint8_t *i = pStart; i < pEnd; ++i) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (*i != static_cast('.')) { + buffer += *i; + } + } + + // 11. convert none-prefix radix string + return Strtod(buffer.c_str(), exponent, radix); +} + +double NumberHelper::Strtod(const char *str, int exponent, uint8_t radix) +{ + ASSERT(str != nullptr); + auto p = const_cast(str); + Sign sign = Sign::NONE; + uint64_t number = 0; + uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix; + double result = 0.0; + if (*p == '-') { + sign = Sign::NEG; + ++p; + } + while (*p == '0') { + ++p; + } + while (*p != '\0') { + uint8_t digit = ToDigit(static_cast(*p)); + if (digit >= radix) { + break; + } + if (number < numberMax) { + number = number * radix + digit; + } else { + ++exponent; + } + ++p; + } + if (exponent < 0) { + result = number / std::pow(radix, -exponent); + } else { + result = number * std::pow(radix, exponent); + } + return sign == Sign::NEG ? -result : result; +} + +int32_t NumberHelper::DoubleToInt(double d, size_t bits) +{ + int32_t ret = 0; + auto u64 = bit_cast(d); + int exp = static_cast((u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE) - DOUBLE_EXPONENT_BIAS; + if (exp < static_cast(bits - 1)) { + // smaller than INT_MAX, fast conversion + ret = static_cast(d); + } else if (exp < static_cast(bits + DOUBLE_SIGNIFICAND_SIZE)) { + // Still has significand bits after mod 2^ + // Get low bits by shift left <64 - bits> and shift right <64 - bits> + uint64_t value = (((u64 & DOUBLE_SIGNIFICAND_MASK) | DOUBLE_HIDDEN_BIT) + << (exp - DOUBLE_SIGNIFICAND_SIZE + INT64_BITS - bits)) >> + (INT64_BITS - bits); + ret = static_cast(value); + if ((u64 & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK && ret != INT32_MIN) { + ret = -ret; + } + } else { + // No significand bits after mod 2^, contains NaN and INF + ret = 0; + } + return ret; +} + +int32_t NumberHelper::DoubleInRangeInt32(double d) +{ + if (d > INT_MAX) { + return INT_MAX; + } + if (d < INT_MIN) { + return INT_MIN; + } + return base::NumberHelper::DoubleToInt(d, base::INT32_BITS); +} +} // namespace panda::ecmascript::base diff --git a/ecmascript/base/number_helper.h b/ecmascript/base/number_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..b4c96cb2b81029ce72c7a2edfe39f6937a71bab2 --- /dev/null +++ b/ecmascript/base/number_helper.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BASE_NUMBER_HELPER_H +#define PANDA_RUNTIME_ECMASCRIPT_BASE_NUMBER_HELPER_H + +#include +#include "ecmascript/ecma_string.h" + +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript::base { +// NOLINTNEXTLINE(modernize-avoid-c-arrays) +static constexpr uint16_t SPACE_OR_LINE_TERMINAL[] = { + 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x0020, 0x00A0, 0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, + 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x2028, 0x2029, 0x202F, 0x205F, 0x3000, 0xFEFF, +}; + +constexpr double MIN_RADIX = 2; +constexpr double MAX_RADIX = 36; +constexpr double MIN_FRACTION = 0; +constexpr double MAX_FRACTION = 100; + +// Coversion flags +static constexpr uint32_t NO_FLAGS = 0U; +static constexpr uint32_t ALLOW_BINARY = 1U << 0U; +static constexpr uint32_t ALLOW_OCTAL = 1U << 1U; +static constexpr uint32_t ALLOW_HEX = 1U << 2U; +static constexpr uint32_t IGNORE_TRAILING = 1U << 3U; + +static constexpr uint32_t MAX_PRECISION = 16; +static constexpr uint8_t BINARY = 2; +static constexpr uint8_t OCTAL = 8; +static constexpr uint8_t DECIMAL = 10; +static constexpr uint8_t HEXADECIMAL = 16; +static constexpr double HALF = 0.5; +static constexpr double EPSILON = std::numeric_limits::epsilon(); +static constexpr double MAX_SAFE_INTEGER = 9007199254740991; +static constexpr double MAX_VALUE = std::numeric_limits::max(); +static constexpr double MIN_VALUE = std::numeric_limits::min(); +static constexpr double POSITIVE_INFINITY = std::numeric_limits::infinity(); +static constexpr double NAN_VALUE = std::numeric_limits::quiet_NaN(); + +// Helper defines for double +static constexpr int DOUBLE_MAX_PRECISION = 15; +static constexpr int DOUBLE_EXPONENT_BIAS = 0x3FF; +static constexpr size_t DOUBLE_SIGNIFICAND_SIZE = 52; +static constexpr uint64_t DOUBLE_SIGN_MASK = 0x8000000000000000ULL; +static constexpr uint64_t DOUBLE_EXPONENT_MASK = 0x7FFULL << DOUBLE_SIGNIFICAND_SIZE; +static constexpr uint64_t DOUBLE_SIGNIFICAND_MASK = 0x000FFFFFFFFFFFFFULL; +static constexpr uint64_t DOUBLE_HIDDEN_BIT = 1ULL << DOUBLE_SIGNIFICAND_SIZE; + +static constexpr size_t INT64_BITS = 64; +static constexpr size_t INT32_BITS = 32; +static constexpr size_t INT16_BITS = 16; +static constexpr size_t INT8_BITS = 8; + +class NumberHelper { +public: + static bool IsFinite(JSTaggedValue number) + { + return number.IsInt() || (number.IsDouble() && std::isfinite(number.GetDouble())); + } + static bool IsNaN(JSTaggedValue number) + { + return number.IsDouble() && std::isnan(number.GetDouble()); + } + static JSTaggedValue DoubleToString(JSThread *thread, double number, int radix); + static bool IsEmptyString(const uint8_t *start, const uint8_t *end); + static JSHandle NumberToString(const JSThread *thread, JSTaggedValue number); + static double TruncateDouble(double d); + static double StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags = NO_FLAGS); + static int32_t DoubleToInt(double d, size_t bits); + static int32_t DoubleInRangeInt32(double d); + static JSTaggedValue DoubleToExponential(JSThread *thread, double number, int digit); + static JSTaggedValue DoubleToFixed(JSThread *thread, double number, int digit); + static JSTaggedValue DoubleToPrecision(JSThread *thread, double number, int digit); + static JSTaggedValue StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix); + static CString IntToString(int number); + +private: + static char Carry(char current, int radix); + static double Strtod(const char *str, int exponent, uint8_t radix); + static CString IntergerToString(double number, int radix); + static CString DecimalsToString(double *numberInteger, double fraction, int radix, double delta); + static bool IsNonspace(uint16_t c); + static bool GotoNonspace(uint8_t **ptr, const uint8_t *end); +}; +} // namespace panda::ecmascript::base +#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_NUMBER_HELPER_H diff --git a/ecmascript/base/string_helper.cpp b/ecmascript/base/string_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b82916fa6935d0d3d5b3c075bbf01f302355b7c --- /dev/null +++ b/ecmascript/base/string_helper.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/string_helper.h" +#include "ecmascript/ecma_string-inl.h" + +namespace panda::ecmascript::base { +std::string StringHelper::ToStdString(EcmaString *string) +{ + return std::string(ConvertToString(string)); +} + +bool StringHelper::CheckDuplicate(EcmaString *string) +{ + if (string->IsUtf8()) { + const uint8_t *array = string->GetDataUtf8(); + size_t length = string->GetUtf8Length() - 1; + std::bitset bitSet; + for (size_t i = 0; i < length; ++i) { + char idx = *array; + if (bitSet.test(idx)) { + return true; + } + bitSet.set(idx); + array++; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } else { + UNREACHABLE(); + } + return false; +} + +EcmaString *StringHelper::Repeat(JSThread *thread, const std::u16string &thisStr, int32_t repeatLen) +{ + ecmascript::ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (repeatLen == 0) { + return *factory->GetEmptyString(); // Create empty EcmaString. + } + std::u16string tmpStr = thisStr; + for (int32_t i = 1; i < repeatLen; i++) { + tmpStr.append(thisStr); + } + const char16_t *constChar16tData = tmpStr.data(); + auto *char16tData = const_cast(constChar16tData); + auto *uint16tData = reinterpret_cast(char16tData); + int32_t length = tmpStr.size(); + return *factory->NewFromUtf16(uint16tData, length); +} + +EcmaString *StringHelper::Trim(JSThread *thread, const std::u16string &thisStr) +{ + ecmascript::ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + std::u16string tmpStr = thisStr; + if (tmpStr.empty()) { + return *factory->GetEmptyString(); + } + std::string str = U16stringToString(tmpStr); + std::wstring wstr = StringToWstring(str); + std::wregex r( + L"^[" + L"\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007" + L"\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+|[" + L"\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007" + L"\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+$"); + wstr = regex_replace(wstr, r, L""); + str = WstringToString(wstr); + tmpStr = StringToU16string(str); + const char16_t *constChar16tData = tmpStr.data(); + auto *char16tData = const_cast(constChar16tData); + auto *uint16tData = reinterpret_cast(char16tData); + int32_t length = tmpStr.size(); + return *factory->NewFromUtf16(uint16tData, length); +} +} // namespace panda::ecmascript::base diff --git a/ecmascript/base/string_helper.h b/ecmascript/base/string_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..b435555e81b2b93d318413c9cbedd838335dc321 --- /dev/null +++ b/ecmascript/base/string_helper.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BASE_STRING_HELP_H +#define PANDA_RUNTIME_ECMASCRIPT_BASE_STRING_HELP_H + +#include +#include +#include +#include +#include +#include + +#include "ecmascript/base/utf_helper.h" +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/assert_scope-inl.h" +#include "ecmascript/object_factory.h" +#include "libpandafile/file_items.h" +#include "unicode/unistr.h" + +namespace panda::ecmascript::base { +class StringHelper { +public: + static std::string ToStdString(EcmaString *string); + + static bool CheckDuplicate(EcmaString *string); + + static inline bool Contains(const EcmaString *string, const EcmaString *other) + { + [[maybe_unused]] DisallowGarbageCollection noGc; + CString str = ConvertToString(string, StringConvertedUsage::LOGICOPERATION); + CString oth = ConvertToString(other, StringConvertedUsage::LOGICOPERATION); + CString::size_type index = str.find(oth); + return (index != CString::npos); + } + + static inline CString RepalceAll(CString str, const CString &oldValue, + const CString &newValue) + { + if (oldValue.empty() || oldValue == newValue) { + return str; + } + CString::size_type pos(0); + while ((pos = str.find(oldValue, pos)) != CString::npos) { + str.replace(pos, oldValue.length(), newValue); + pos += newValue.length(); + } + return str; + } + + static inline std::string SubString(JSThread *thread, const JSHandle &string, uint32_t start, + uint32_t length) + { + EcmaString *substring = EcmaString::FastSubString(string, start, length, thread->GetEcmaVM()); + return std::string(ConvertToString(substring, StringConvertedUsage::LOGICOPERATION)); + } + + static inline std::u16string Utf16ToU16String(const uint16_t *utf16Data, uint32_t dataLen) + { + auto *char16tData = reinterpret_cast(utf16Data); + std::u16string u16str(char16tData, dataLen); + return u16str; + } + + static inline std::u16string Utf8ToU16String(const uint8_t *utf8Data, uint32_t dataLen) + { + auto *charData = reinterpret_cast(utf8Data); + std::string str(charData, dataLen); + std::u16string u16str = std::wstring_convert, char16_t>{}.from_bytes(str); + return u16str; + } + + static inline std::string WstringToString(const std::wstring &wstr) + { + return std::wstring_convert, wchar_t>{}.to_bytes(wstr); + } + + static inline std::wstring StringToWstring(const std::string &str) + { + return std::wstring_convert, wchar_t>{}.from_bytes(str); + } + + static inline std::string U16stringToString(const std::u16string &u16str) + { + return std::wstring_convert, char16_t>{}.to_bytes(u16str); + } + + static inline std::u16string StringToU16string(const std::string &str) + { + return std::wstring_convert, char16_t>{}.from_bytes(str); + } + + static inline size_t Find(const std::u16string &thisStr, const std::u16string &searchStr, int32_t pos) + { + size_t idx = thisStr.find(searchStr, pos); + return idx; + } + + static inline size_t RFind(const std::u16string &thisStr, const std::u16string &searchStr, int32_t pos) + { + size_t idx = thisStr.rfind(searchStr, pos); + return idx; + } + + static inline EcmaString *ToUpper(JSThread *thread, const std::u16string &str) + { + ecmascript::ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + std::u16string tmpStr = str; + const char16_t *constChar16tData = tmpStr.data(); + icu::UnicodeString uString(constChar16tData); + icu::UnicodeString up = uString.toUpper(); + std::string res; + up.toUTF8String(res); + return *factory->NewFromStdString(res); + } + + static inline EcmaString *ToLower(JSThread *thread, const std::u16string &str) + { + ecmascript::ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + std::u16string tmpStr = str; + const char16_t *constChar16tData = tmpStr.data(); + icu::UnicodeString uString(constChar16tData); + icu::UnicodeString low = uString.toLower(); + std::string res; + low.toUTF8String(res); + return *factory->NewFromStdString(res); + } + + static inline size_t FindFromU16ToUpper(const std::u16string &thisStr, uint16_t *u16Data) + { + std::u16string tmpStr = Utf16ToU16String(u16Data, 1); + const char16_t *constChar16tData = tmpStr.data(); + icu::UnicodeString uString(constChar16tData); + icu::UnicodeString up = uString.toUpper(); + std::string res; + up.toUTF8String(res); + std::u16string searchStr = StringToU16string(res); + size_t idx = Find(thisStr, searchStr, 0); + return idx; + } + + static EcmaString *Repeat(JSThread *thread, const std::u16string &thisStr, int32_t repeatLen); + + static EcmaString *Trim(JSThread *thread, const std::u16string &thisStr); + + static inline std::u16string Append(const std::u16string &str1, const std::u16string &str2) + { + std::u16string tmpStr = str1; + return tmpStr.append(str2); + } + + static inline uint32_t Utf8ToU32String(const std::vector &data) + { + std::string str(data.begin(), data.end()); + std::u32string u32str = std::wstring_convert, char32_t>{}.from_bytes(str); + auto u32data = reinterpret_cast(u32str.data()); + return *u32data; + } + + static inline std::string Utf32ToString(uint32_t u32Data) + { + UChar32 charData = u32Data; + icu::UnicodeString uString(charData); + std::string res; + uString.toUTF8String(res); + return res; + } +}; +} // namespace panda::ecmascript::base +#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_STRING_HELP_H diff --git a/ecmascript/base/typed_array_helper-inl.h b/ecmascript/base/typed_array_helper-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..b7d483149027d906b9a4a3deae511f2364cc0bed --- /dev/null +++ b/ecmascript/base/typed_array_helper-inl.h @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_INL_H + +#include "ecmascript/builtins/builtins_arraybuffer.h" +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/base/typed_array_helper.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_object.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_int8_array.h" +#include "ecmascript/js_uint8_array.h" +#include "ecmascript/js_uint8_clamped_array.h" +#include "ecmascript/js_int16_array.h" +#include "ecmascript/js_uint16_array.h" +#include "ecmascript/js_int32_array.h" +#include "ecmascript/js_uint32_array.h" +#include "ecmascript/js_float32_array.h" +#include "ecmascript/js_float64_array.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/base/error_helper.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/base/error_type.h" + +namespace panda::ecmascript::base { +DataViewType TypedArrayHelper::GetType(const JSHandle &obj) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + return DataViewType::INT8; + case JSType::JS_UINT8_ARRAY: + return DataViewType::UINT8; + case JSType::JS_UINT8_CLAMPED_ARRAY: + return DataViewType::UINT8_CLAMPED; + case JSType::JS_INT16_ARRAY: + return DataViewType::INT16; + case JSType::JS_UINT16_ARRAY: + return DataViewType::UINT16; + case JSType::JS_INT32_ARRAY: + return DataViewType::INT32; + case JSType::JS_UINT32_ARRAY: + return DataViewType::UINT32; + case JSType::JS_FLOAT32_ARRAY: + return DataViewType::FLOAT32; + default: + return DataViewType::FLOAT64; + } +} + +int32_t TypedArrayHelper::GetElementSize(const JSHandle &obj) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + case JSType::JS_UINT8_ARRAY: + case JSType::JS_UINT8_CLAMPED_ARRAY: + return ElementSize::ONE; + case JSType::JS_INT16_ARRAY: + case JSType::JS_UINT16_ARRAY: + return ElementSize::TWO; + case JSType::JS_INT32_ARRAY: + case JSType::JS_UINT32_ARRAY: + case JSType::JS_FLOAT32_ARRAY: + return ElementSize::FOUR; + default: + return ElementSize::EIGHT; + } +} + +DataViewType TypedArrayHelper::GetTypeFromName(JSThread *thread, const JSHandle &typeName) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt8ArrayString())) { + return DataViewType::INT8; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ArrayString())) { + return DataViewType::UINT8; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ClampedArrayString())) { + return DataViewType::UINT8_CLAMPED; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt16ArrayString())) { + return DataViewType::INT16; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint16ArrayString())) { + return DataViewType::UINT16; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt32ArrayString())) { + return DataViewType::INT32; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint32ArrayString())) { + return DataViewType::UINT32; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat32ArrayString())) { + return DataViewType::FLOAT32; + } + return DataViewType::FLOAT64; +} + +JSHandle TypedArrayHelper::GetConstructor(JSThread *thread, const JSHandle &obj) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSType type = obj->GetTaggedObject()->GetClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + return env->GetInt8ArrayFunction(); + case JSType::JS_UINT8_ARRAY: + return env->GetUint8ArrayFunction(); + case JSType::JS_UINT8_CLAMPED_ARRAY: + return env->GetUint8ClampedArrayFunction(); + case JSType::JS_INT16_ARRAY: + return env->GetInt16ArrayFunction(); + case JSType::JS_UINT16_ARRAY: + return env->GetUint16ArrayFunction(); + case JSType::JS_INT32_ARRAY: + return env->GetInt32ArrayFunction(); + case JSType::JS_UINT32_ARRAY: + return env->GetUint32ArrayFunction(); + case JSType::JS_FLOAT32_ARRAY: + return env->GetFloat32ArrayFunction(); + default: + return env->GetFloat64ArrayFunction(); + } +} + +JSHandle TypedArrayHelper::GetConstructorFromName(JSThread *thread, const JSHandle &typeName) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + auto globalConst = thread->GlobalConstants(); + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt8ArrayString())) { + return JSHandle(env->GetInt8ArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ArrayString())) { + return JSHandle(env->GetUint8ArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ClampedArrayString())) { + return JSHandle(env->GetUint8ClampedArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt16ArrayString())) { + return JSHandle(env->GetInt16ArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint16ArrayString())) { + return JSHandle(env->GetUint16ArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt32ArrayString())) { + return JSHandle(env->GetInt32ArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint32ArrayString())) { + return JSHandle(env->GetUint32ArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat32ArrayString())) { + return JSHandle(env->GetFloat32ArrayFunction()); + } + return JSHandle(env->GetFloat64ArrayFunction()); +} + +int32_t TypedArrayHelper::GetSizeFromName(JSThread *thread, const JSHandle &typeName) +{ + int32_t elementSize; + auto globalConst = thread->GlobalConstants(); + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt8ArrayString()) || + JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ArrayString()) || + JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ClampedArrayString())) { + elementSize = ElementSize::ONE; + } else if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt16ArrayString()) || + JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint16ArrayString())) { + elementSize = ElementSize::TWO; + } else if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt32ArrayString()) || + JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint32ArrayString()) || + JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat32ArrayString())) { + elementSize = ElementSize::FOUR; + } else { + elementSize = ElementSize::EIGHT; + } + return elementSize; +} + +void TypedArrayHelper::SetViewedArrayBuffer(JSThread *thread, const JSHandle &obj, + JSTaggedValue viewedArrayBuffer) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(*obj)->SetViewedArrayBuffer(thread, viewedArrayBuffer); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(*obj)->SetViewedArrayBuffer(thread, viewedArrayBuffer); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(*obj)->SetViewedArrayBuffer(thread, viewedArrayBuffer); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(*obj)->SetViewedArrayBuffer(thread, viewedArrayBuffer); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(*obj)->SetViewedArrayBuffer(thread, viewedArrayBuffer); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(*obj)->SetViewedArrayBuffer(thread, viewedArrayBuffer); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(*obj)->SetViewedArrayBuffer(thread, viewedArrayBuffer); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(*obj)->SetViewedArrayBuffer(thread, viewedArrayBuffer); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(*obj)->SetViewedArrayBuffer(thread, viewedArrayBuffer); + break; + default: + break; + } +} + +void TypedArrayHelper::SetTypedArrayName(JSThread *thread, const JSHandle &obj, + const JSHandle &typedArrayName) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(*obj)->SetTypedArrayName(thread, typedArrayName); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(*obj)->SetTypedArrayName(thread, typedArrayName); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(*obj)->SetTypedArrayName(thread, typedArrayName); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(*obj)->SetTypedArrayName(thread, typedArrayName); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(*obj)->SetTypedArrayName(thread, typedArrayName); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(*obj)->SetTypedArrayName(thread, typedArrayName); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(*obj)->SetTypedArrayName(thread, typedArrayName); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(*obj)->SetTypedArrayName(thread, typedArrayName); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(*obj)->SetTypedArrayName(thread, typedArrayName); + break; + default: + break; + } +} + +void TypedArrayHelper::SetByteLength(JSThread *thread, const JSHandle &obj, int32_t byteLength) +{ + auto byteLengthValue = JSTaggedValue(byteLength); + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(*obj)->SetByteLength(thread, byteLengthValue); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(*obj)->SetByteLength(thread, byteLengthValue); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(*obj)->SetByteLength(thread, byteLengthValue); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(*obj)->SetByteLength(thread, byteLengthValue); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(*obj)->SetByteLength(thread, byteLengthValue); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(*obj)->SetByteLength(thread, byteLengthValue); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(*obj)->SetByteLength(thread, byteLengthValue); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(*obj)->SetByteLength(thread, byteLengthValue); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(*obj)->SetByteLength(thread, byteLengthValue); + break; + default: + break; + } +} + +void TypedArrayHelper::SetByteOffset(JSThread *thread, const JSHandle &obj, int32_t byteOffset) +{ + auto byteOffsetValue = JSTaggedValue(byteOffset); + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(*obj)->SetByteOffset(thread, byteOffsetValue); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(*obj)->SetByteOffset(thread, byteOffsetValue); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(*obj)->SetByteOffset(thread, byteOffsetValue); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(*obj)->SetByteOffset(thread, byteOffsetValue); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(*obj)->SetByteOffset(thread, byteOffsetValue); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(*obj)->SetByteOffset(thread, byteOffsetValue); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(*obj)->SetByteOffset(thread, byteOffsetValue); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(*obj)->SetByteOffset(thread, byteOffsetValue); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(*obj)->SetByteOffset(thread, byteOffsetValue); + break; + default: + break; + } +} + +void TypedArrayHelper::SetArrayLength(JSThread *thread, const JSHandle &obj, int32_t arrayLength) +{ + auto arrayLengthValue = JSTaggedValue(arrayLength); + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(*obj)->SetArrayLength(thread, arrayLengthValue); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(*obj)->SetArrayLength(thread, arrayLengthValue); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(*obj)->SetArrayLength(thread, arrayLengthValue); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(*obj)->SetArrayLength(thread, arrayLengthValue); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(*obj)->SetArrayLength(thread, arrayLengthValue); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(*obj)->SetArrayLength(thread, arrayLengthValue); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(*obj)->SetArrayLength(thread, arrayLengthValue); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(*obj)->SetArrayLength(thread, arrayLengthValue); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(*obj)->SetArrayLength(thread, arrayLengthValue); + break; + default: + break; + } +} + +JSTaggedValue TypedArrayHelper::GetViewedArrayBuffer(const JSHandle &obj) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + JSTaggedValue buffer; + switch (type) { + case JSType::JS_INT8_ARRAY: + buffer = JSInt8Array::Cast(*obj)->GetViewedArrayBuffer(); + break; + case JSType::JS_UINT8_ARRAY: + buffer = JSUint8Array::Cast(*obj)->GetViewedArrayBuffer(); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + buffer = JSUint8ClampedArray::Cast(*obj)->GetViewedArrayBuffer(); + break; + case JSType::JS_INT16_ARRAY: + buffer = JSInt16Array::Cast(*obj)->GetViewedArrayBuffer(); + break; + case JSType::JS_UINT16_ARRAY: + buffer = JSUint16Array::Cast(*obj)->GetViewedArrayBuffer(); + break; + case JSType::JS_INT32_ARRAY: + buffer = JSInt32Array::Cast(*obj)->GetViewedArrayBuffer(); + break; + case JSType::JS_UINT32_ARRAY: + buffer = JSUint32Array::Cast(*obj)->GetViewedArrayBuffer(); + break; + case JSType::JS_FLOAT32_ARRAY: + buffer = JSFloat32Array::Cast(*obj)->GetViewedArrayBuffer(); + break; + case JSType::JS_FLOAT64_ARRAY: + buffer = JSFloat64Array::Cast(*obj)->GetViewedArrayBuffer(); + break; + default: + break; + } + return buffer; +} + +JSHandle TypedArrayHelper::GetTypedArrayName(JSThread *thread, const JSHandle &obj) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + JSTaggedValue name; + switch (type) { + case JSType::JS_INT8_ARRAY: + name = JSInt8Array::Cast(*obj)->GetTypedArrayName(); + break; + case JSType::JS_UINT8_ARRAY: + name = JSUint8Array::Cast(*obj)->GetTypedArrayName(); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + name = JSUint8ClampedArray::Cast(*obj)->GetTypedArrayName(); + break; + case JSType::JS_INT16_ARRAY: + name = JSInt16Array::Cast(*obj)->GetTypedArrayName(); + break; + case JSType::JS_UINT16_ARRAY: + name = JSUint16Array::Cast(*obj)->GetTypedArrayName(); + break; + case JSType::JS_INT32_ARRAY: + name = JSInt32Array::Cast(*obj)->GetTypedArrayName(); + break; + case JSType::JS_UINT32_ARRAY: + name = JSUint32Array::Cast(*obj)->GetTypedArrayName(); + break; + case JSType::JS_FLOAT32_ARRAY: + name = JSFloat32Array::Cast(*obj)->GetTypedArrayName(); + break; + case JSType::JS_FLOAT64_ARRAY: + name = JSFloat64Array::Cast(*obj)->GetTypedArrayName(); + break; + default: + break; + } + return JSHandle(thread, name); +} + +int32_t TypedArrayHelper::GetByteLength(JSThread *thread, const JSHandle &obj) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + JSTaggedValue length; + switch (type) { + case JSType::JS_INT8_ARRAY: + length = JSInt8Array::Cast(*obj)->GetByteLength(); + break; + case JSType::JS_UINT8_ARRAY: + length = JSUint8Array::Cast(*obj)->GetByteLength(); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + length = JSUint8ClampedArray::Cast(*obj)->GetByteLength(); + break; + case JSType::JS_INT16_ARRAY: + length = JSInt16Array::Cast(*obj)->GetByteLength(); + break; + case JSType::JS_UINT16_ARRAY: + length = JSUint16Array::Cast(*obj)->GetByteLength(); + break; + case JSType::JS_INT32_ARRAY: + length = JSInt32Array::Cast(*obj)->GetByteLength(); + break; + case JSType::JS_UINT32_ARRAY: + length = JSUint32Array::Cast(*obj)->GetByteLength(); + break; + case JSType::JS_FLOAT32_ARRAY: + length = JSFloat32Array::Cast(*obj)->GetByteLength(); + break; + case JSType::JS_FLOAT64_ARRAY: + length = JSFloat64Array::Cast(*obj)->GetByteLength(); + break; + default: + break; + } + return JSTaggedValue::ToLength(thread, JSHandle(thread, length)).ToInt32(); +} + +int32_t TypedArrayHelper::GetByteOffset(JSThread *thread, const JSHandle &obj) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + JSTaggedValue length; + switch (type) { + case JSType::JS_INT8_ARRAY: + length = JSInt8Array::Cast(*obj)->GetByteOffset(); + break; + case JSType::JS_UINT8_ARRAY: + length = JSUint8Array::Cast(*obj)->GetByteOffset(); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + length = JSUint8ClampedArray::Cast(*obj)->GetByteOffset(); + break; + case JSType::JS_INT16_ARRAY: + length = JSInt16Array::Cast(*obj)->GetByteOffset(); + break; + case JSType::JS_UINT16_ARRAY: + length = JSUint16Array::Cast(*obj)->GetByteOffset(); + break; + case JSType::JS_INT32_ARRAY: + length = JSInt32Array::Cast(*obj)->GetByteOffset(); + break; + case JSType::JS_UINT32_ARRAY: + length = JSUint32Array::Cast(*obj)->GetByteOffset(); + break; + case JSType::JS_FLOAT32_ARRAY: + length = JSFloat32Array::Cast(*obj)->GetByteOffset(); + break; + case JSType::JS_FLOAT64_ARRAY: + length = JSFloat64Array::Cast(*obj)->GetByteOffset(); + break; + default: + break; + } + return JSTaggedValue::ToLength(thread, JSHandle(thread, length)).ToInt32(); +} + +int32_t TypedArrayHelper::GetArrayLength(JSThread *thread, const JSHandle &obj) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + JSTaggedValue length; + switch (type) { + case JSType::JS_INT8_ARRAY: + length = JSInt8Array::Cast(*obj)->GetArrayLength(); + break; + case JSType::JS_UINT8_ARRAY: + length = JSUint8Array::Cast(*obj)->GetArrayLength(); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + length = JSUint8ClampedArray::Cast(*obj)->GetArrayLength(); + break; + case JSType::JS_INT16_ARRAY: + length = JSInt16Array::Cast(*obj)->GetArrayLength(); + break; + case JSType::JS_UINT16_ARRAY: + length = JSUint16Array::Cast(*obj)->GetArrayLength(); + break; + case JSType::JS_INT32_ARRAY: + length = JSInt32Array::Cast(*obj)->GetArrayLength(); + break; + case JSType::JS_UINT32_ARRAY: + length = JSUint32Array::Cast(*obj)->GetArrayLength(); + break; + case JSType::JS_FLOAT32_ARRAY: + length = JSFloat32Array::Cast(*obj)->GetArrayLength(); + break; + case JSType::JS_FLOAT64_ARRAY: + length = JSFloat64Array::Cast(*obj)->GetArrayLength(); + break; + default: + break; + } + return JSTaggedValue::ToLength(thread, JSHandle(thread, length)).ToInt32(); +} +} // namespace panda::ecmascript::base + +#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_INL_H diff --git a/ecmascript/base/typed_array_helper.cpp b/ecmascript/base/typed_array_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6205a51a179ab648303d32fbc690d96f4e810fd2 --- /dev/null +++ b/ecmascript/base/typed_array_helper.cpp @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/typed_array_helper.h" +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/base/error_helper.h" +#include "ecmascript/base/error_type.h" +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/builtins/builtins_arraybuffer.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_float32_array.h" +#include "ecmascript/js_float64_array.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_int16_array.h" +#include "ecmascript/js_int32_array.h" +#include "ecmascript/js_int8_array.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_typed_array.h" +#include "ecmascript/js_uint16_array.h" +#include "ecmascript/js_uint32_array.h" +#include "ecmascript/js_uint8_array.h" +#include "ecmascript/js_uint8_clamped_array.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript::base { +using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; + +// es11 22.2.4 The TypedArray Constructors +JSTaggedValue TypedArrayHelper::TypedArrayConstructor(EcmaRuntimeCallInfo *argv, + const JSHandle &constructorName) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle newTarget = BuiltinsBase::GetNewTarget(argv); + // 2. If NewTarget is undefined, throw a TypeError exception. + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The NewTarget is undefined.", JSTaggedValue::Exception()); + } + // 3. Let constructorName be the String value of the Constructor Name value specified in Table 61 for this + // TypedArray constructor. + // 4. Let O be ? AllocateTypedArray(constructorName, NewTarget, "%TypedArray.prototype%"). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle firstArg = BuiltinsBase::GetCallArg(argv, 0); + if (!firstArg->IsECMAObject()) { + // es11 22.2.4.1 TypedArray ( ) + int32_t elementLength = 0; + // es11 22.2.4.2 TypedArray ( length ) + if (!firstArg->IsUndefined()) { + JSTaggedNumber index = JSTaggedValue::ToIndex(thread, firstArg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + elementLength = static_cast(index.GetNumber()); + } + JSHandle obj = + TypedArrayHelper::AllocateTypedArray(factory, ecmaVm, constructorName, newTarget, elementLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return obj.GetTaggedValue(); + } + JSHandle obj = TypedArrayHelper::AllocateTypedArray(factory, ecmaVm, constructorName, newTarget); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (firstArg->IsTypedArray()) { + return TypedArrayHelper::CreateFromTypedArray(argv, obj, constructorName); + } + if (firstArg->IsArrayBuffer()) { + return TypedArrayHelper::CreateFromArrayBuffer(argv, obj, constructorName); + } + return TypedArrayHelper::CreateFromOrdinaryObject(argv, obj); +} + +// es11 22.2.4.4 TypedArray ( object ) +JSTaggedValue TypedArrayHelper::CreateFromOrdinaryObject(EcmaRuntimeCallInfo *argv, const JSHandle &obj) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + JSHandle objectArg = BuiltinsBase::GetCallArg(argv, 0); + JSHandle object(objectArg); + // 5. Let usingIterator be ? GetMethod(object, @@iterator). + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle usingIterator = + JSObject::GetMethod(thread, JSHandle::Cast(object), iteratorSymbol); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. If usingIterator is not undefined, then + if (!usingIterator->IsUndefined()) { + CVector> vec; + // a. Let values be ? IterableToList(object, usingIterator). + // b. Let len be the number of elements in values. + // c. Perform ? AllocateTypedArrayBuffer(O, len). + JSHandle iterator = JSIterator::GetIterator(thread, objectArg, usingIterator); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle next(thread, JSTaggedValue::True()); + while (!next->IsFalse()) { + next = JSIterator::IteratorStep(thread, iterator); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!next->IsFalse()) { + JSHandle nextValue = JSIterator::IteratorValue(thread, next); + vec.push_back(nextValue); + } + } + int32_t len = vec.size(); + TypedArrayHelper::AllocateTypedArrayBuffer(thread, ecmaVm, obj, len); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // d. Let k be 0. + // e. Repeat, while k < len + // i. Let Pk be ! ToString(k). + // ii. Let kValue be the first element of values and remove that element from values. + // iii. Perform ? Set(O, Pk, kValue, true). + // iv. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < len) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSHandle kValue = vec[k]; + JSTaggedValue::SetProperty(thread, JSHandle::Cast(obj), kKey, kValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // f. Assert: values is now an empty List. + // g. Return O. + return obj.GetTaggedValue(); + } + + // 7. NOTE: object is not an Iterable so assume it is already an array-like object. + // 8. Let arrayLike be object. + // 9. Let len be ? LengthOfArrayLike(arrayLike). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSTaggedNumber lenTemp = + JSTaggedValue::ToLength(thread, JSObject::GetProperty(thread, objectArg, lengthKey).GetValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double len = lenTemp.GetNumber(); + // 10. Perform ? AllocateTypedArrayBuffer(O, len). + TypedArrayHelper::AllocateTypedArrayBuffer(thread, ecmaVm, obj, len); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11. Let k be 0. + // 12. Repeat, while k < len + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(arrayLike, Pk). + // c. Perform ? Set(O, Pk, kValue, true). + // d. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < len) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSHandle kValue = JSObject::GetProperty(thread, objectArg, kKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(obj), kKey, kValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // 13. Return O. + return obj.GetTaggedValue(); +} + +// es11 22.2.4.3 TypedArray ( typedArray ) +JSTaggedValue TypedArrayHelper::CreateFromTypedArray(EcmaRuntimeCallInfo *argv, const JSHandle &obj, + const JSHandle &constructorName) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 5. Let srcArray be typedArray. + JSHandle srcArray = BuiltinsBase::GetCallArg(argv, 0); + JSHandle srcObj(srcArray); + // 6. Let srcData be srcArray.[[ViewedArrayBuffer]]. + JSHandle srcData(thread, TypedArrayHelper::GetViewedArrayBuffer(srcObj)); + // 7. If IsDetachedBuffer(srcData) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(srcData.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The srcData is detached buffer.", JSTaggedValue::Exception()); + } + // 8. Let elementType be the Element Type value in Table 61 for constructorName. + DataViewType elementType = TypedArrayHelper::GetTypeFromName(thread, constructorName); + // 9. Let elementLength be srcArray.[[ArrayLength]]. + // 10. Let srcName be the String value of srcArray.[[TypedArrayName]]. + // 11. Let srcType be the Element Type value in Table 61 for srcName. + // 12. Let srcElementSize be the Element Size value specified in Table 61 for srcName. + int32_t elementLength = TypedArrayHelper::GetArrayLength(thread, srcObj); + JSHandle srcName = TypedArrayHelper::GetTypedArrayName(thread, srcObj); + DataViewType srcType = TypedArrayHelper::GetTypeFromName(thread, srcName); + int32_t srcElementSize = TypedArrayHelper::GetSizeFromName(thread, srcName); + // 13. Let srcByteOffset be srcArray.[[ByteOffset]]. + // 14. Let elementSize be the Element Size value specified in Table 61 for constructorName. + // 15. Let byteLength be elementSize × elementLength. + int32_t srcByteOffset = TypedArrayHelper::GetByteOffset(thread, srcObj); + int32_t elementSize = TypedArrayHelper::GetSizeFromName(thread, constructorName); + int32_t byteLength = elementSize * elementLength; + // 16. If IsSharedArrayBuffer(srcData) is false, then + // a. Let bufferConstructor be ? SpeciesConstructor(srcData, %ArrayBuffer%). + + JSTaggedValue data; + // 18. If elementType is the same as srcType, then + // a. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength, bufferConstructor). + if (elementType == srcType) { + data = + BuiltinsArrayBuffer::CloneArrayBuffer(thread, srcData, srcByteOffset, globalConst->GetHandledUndefined()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + // 19. Else, + // a. Let data be ? AllocateArrayBuffer(bufferConstructor, byteLength). + JSHandle bufferConstructor = + JSObject::SpeciesConstructor(thread, JSHandle(srcData), env->GetArrayBufferFunction()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + data = BuiltinsArrayBuffer::AllocateArrayBuffer(thread, bufferConstructor, byteLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // b. If IsDetachedBuffer(srcData) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(srcData.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The srcData is detached buffer.", JSTaggedValue::Exception()); + } + // d. Let srcByteIndex be srcByteOffset. + // e. Let targetByteIndex be 0. + int32_t srcByteIndex = srcByteOffset; + int32_t targetByteIndex = 0; + // f. Let count be elementLength. + // g. Repeat, while count > 0 + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + for (int32_t count = elementLength; count > 0; count--) { + // i. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, Unordered). + JSTaggedValue taggedData = + BuiltinsArrayBuffer::GetValueFromBuffer(srcData.GetTaggedValue(), srcByteIndex, srcType, true); + value.Update(taggedData); + JSTaggedNumber numVal = JSTaggedValue::ToNumber(thread, value); + // ii. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, Unordered). + BuiltinsArrayBuffer::SetValueInBuffer(data, targetByteIndex, elementType, numVal, true); + // iii. Set srcByteIndex to srcByteIndex + srcElementSize. + // iv. Set targetByteIndex to targetByteIndex + elementSize. + // v. Set count to count - 1. + srcByteIndex = srcByteIndex + srcElementSize; + targetByteIndex = targetByteIndex + elementSize; + } + } + // 19. Set O’s [[ViewedArrayBuffer]] internal slot to data. + // 20. Set O’s [[ByteLength]] internal slot to byteLength. + // 21. Set O’s [[ByteOffset]] internal slot to 0. + // 22. Set O’s [[ArrayLength]] internal slot to elementLength. + TypedArrayHelper::SetViewedArrayBuffer(thread, obj, data); + TypedArrayHelper::SetByteLength(thread, obj, byteLength); + TypedArrayHelper::SetByteOffset(thread, obj, 0); + TypedArrayHelper::SetArrayLength(thread, obj, elementLength); + // 23. Return O. + return obj.GetTaggedValue(); +} + +// es11 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] ) +JSTaggedValue TypedArrayHelper::CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, const JSHandle &obj, + const JSHandle &constructorName) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 5. Let elementSize be the Element Size value specified in Table 61 for constructorName. + // 6. Let offset be ? ToIndex(byteOffset). + int32_t elementSize = TypedArrayHelper::GetSizeFromName(thread, constructorName); + JSHandle byteOffset = BuiltinsBase::GetCallArg(argv, 1); + JSTaggedNumber index = JSTaggedValue::ToIndex(thread, byteOffset); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + auto offset = static_cast(index.GetNumber()); + // 7. If offset modulo elementSize ≠ 0, throw a RangeError exception. + if (offset % elementSize != 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The offset cannot be an integral multiple of elementSize.", + JSTaggedValue::Exception()); + } + // 8. If length is not undefined, then + // a. Let newLength be ? ToIndex(length). + JSHandle length = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + int32_t newLength = 0; + if (!length->IsUndefined()) { + index = JSTaggedValue::ToIndex(thread, length); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + newLength = static_cast(index.GetNumber()); + } + // 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + JSHandle buffer = BuiltinsBase::GetCallArg(argv, 0); + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The srcData is detached buffer.", JSTaggedValue::Exception()); + } + // 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. + JSTaggedNumber newLengthNum = + JSTaggedNumber::FromIntOrDouble(thread, JSHandle(buffer)->GetArrayBufferByteLength()); + int32_t bufferByteLength = newLengthNum.ToInt32(); + // 11. If length is undefined, then + // a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception. + // b. Let newByteLength be bufferByteLength - offset. + // c. If newByteLength < 0, throw a RangeError exception. + int32_t newByteLength; + if (length->IsUndefined()) { + if (bufferByteLength % elementSize != 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The bufferByteLength cannot be an integral multiple of elementSize.", + JSTaggedValue::Exception()); + } + newByteLength = bufferByteLength - offset; + if (newByteLength < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The newByteLength is less than 0.", JSTaggedValue::Exception()); + } + } else { + // 12. Else, + // a. Let newByteLength be newLength × elementSize. + // b. If offset + newByteLength > bufferByteLength, throw a RangeError exception. + newByteLength = newLength * elementSize; + if (offset + newByteLength > bufferByteLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The newByteLength is out of range.", JSTaggedValue::Exception()); + } + } + // 13. Set O.[[ViewedArrayBuffer]] to buffer. + // 14. Set O.[[ByteLength]] to newByteLength. + // 15. Set O.[[ByteOffset]] to offset. + // 16. Set O.[[ArrayLength]] to newByteLength / elementSize. + TypedArrayHelper::SetViewedArrayBuffer(thread, obj, buffer.GetTaggedValue()); + TypedArrayHelper::SetByteLength(thread, obj, newByteLength); + TypedArrayHelper::SetByteOffset(thread, obj, offset); + TypedArrayHelper::SetArrayLength(thread, obj, newByteLength / elementSize); + // 17. Return O. + return obj.GetTaggedValue(); +} + +// es11 22.2.4.2.1 Runtime Semantics: AllocateTypedArray ( constructorName, newTarget, defaultProto ) +JSHandle TypedArrayHelper::AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm, + const JSHandle &constructorName, + const JSHandle &newTarget) +{ + JSThread *thread = ecmaVm->GetJSThread(); + // 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto). + // 2. Let obj be ! IntegerIndexedObjectCreate(proto). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + JSHandle typedArrayFunc = TypedArrayHelper::GetConstructorFromName(thread, constructorName); + JSHandle obj = factory->NewJSObjectByConstructor(typedArrayFunc, newTarget); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + // 3. Assert: obj.[[ViewedArrayBuffer]] is undefined. + // 4. Set obj.[[TypedArrayName]] to constructorName. + // 7. If length is not present, then + // a. Set obj.[[ByteLength]] to 0. + // b. Set obj.[[ByteOffset]] to 0. + // c. Set obj.[[ArrayLength]] to 0. + TypedArrayHelper::SetTypedArrayName(thread, obj, constructorName); + TypedArrayHelper::SetByteLength(thread, obj, 0); + TypedArrayHelper::SetByteOffset(thread, obj, 0); + TypedArrayHelper::SetArrayLength(thread, obj, 0); + // 9. Return obj. + return obj; +} // namespace panda::ecmascript::base + +// es11 22.2.4.2.1 Runtime Semantics: AllocateTypedArray ( constructorName, newTarget, defaultProto, length ) +JSHandle TypedArrayHelper::AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm, + const JSHandle &constructorName, + const JSHandle &newTarget, int32_t length) +{ + JSThread *thread = ecmaVm->GetJSThread(); + // 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto). + // 2. Let obj be ! IntegerIndexedObjectCreate(proto). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + JSHandle typedArrayFunc = TypedArrayHelper::GetConstructorFromName(thread, constructorName); + JSHandle obj = factory->NewJSObjectByConstructor(typedArrayFunc, newTarget); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + // 3. Assert: obj.[[ViewedArrayBuffer]] is undefined. + // 4. Set obj.[[TypedArrayName]] to constructorName. + TypedArrayHelper::SetTypedArrayName(thread, obj, constructorName); + // 7. If length is not present, then + // 8. Else, + // a. Perform ? AllocateTypedArrayBuffer(obj, length). + TypedArrayHelper::AllocateTypedArrayBuffer(thread, ecmaVm, obj, length); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + // 9. Return obj. + return obj; +} + +// es11 22.2.4.2.2 Runtime Semantics: AllocateTypedArrayBuffer ( O, length ) +JSHandle TypedArrayHelper::AllocateTypedArrayBuffer(JSThread *thread, EcmaVM *ecmaVm, + const JSHandle &obj, double length) +{ + JSHandle env = ecmaVm->GetGlobalEnv(); + // 1. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot. + // 2. Assert: O.[[ViewedArrayBuffer]] is undefined. + // 3. Assert: ! IsNonNegativeInteger(length) is true. + ASSERT(JSTaggedValue(length).IsInteger()); + ASSERT(length >= 0); + JSHandle exception(thread, JSTaggedValue::Exception()); + if (length > JSTypedArray::MAX_TYPED_ARRAY_INDEX) { + THROW_RANGE_ERROR_AND_RETURN(thread, "array length must less than 2^32 - 1", exception); + } + // 4. Let constructorName be the String value of O.[[TypedArrayName]]. + JSHandle constructorName = TypedArrayHelper::GetTypedArrayName(thread, obj); + // 5. Let elementSize be the Element Size value specified in Table 61 for constructorName. + int32_t elementSize = TypedArrayHelper::GetSizeFromName(thread, constructorName); + // 6. Let byteLength be elementSize × length. + double byteLength = elementSize * length; + // 7. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength). + JSHandle constructor = env->GetArrayBufferFunction(); + JSTaggedValue data = BuiltinsArrayBuffer::AllocateArrayBuffer(thread, constructor, byteLength); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, exception); + // 8. Set O.[[ViewedArrayBuffer]] to data. + // 9. Set O.[[ByteLength]] to byteLength. + // 10. Set O.[[ByteOffset]] to 0. + // 11. Set O.[[ArrayLength]] to length. + TypedArrayHelper::SetViewedArrayBuffer(thread, obj, data); + TypedArrayHelper::SetByteLength(thread, obj, byteLength); + TypedArrayHelper::SetByteOffset(thread, obj, 0); + TypedArrayHelper::SetArrayLength(thread, obj, length); + // 12. Return O. + return obj; +} + +// es11 22.2.4.7 TypedArraySpeciesCreate ( exemplar, argumentList ) +JSHandle TypedArrayHelper::TypedArraySpeciesCreate(JSThread *thread, const JSHandle &obj, + const JSHandle &argumentList) +{ + // 1. Assert: exemplar is an Object that has [[TypedArrayName]] and [[ContentType]] internal slots. + // 2. Let defaultConstructor be the intrinsic object listed in column one of Table 61 for + // exemplar.[[TypedArrayName]]. + JSHandle defaultConstructor = TypedArrayHelper::GetConstructor(thread, JSHandle(obj)); + // 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor). + JSHandle thisConstructor = JSObject::SpeciesConstructor(thread, obj, defaultConstructor); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + // 4. Let result be ? TypedArrayCreate(constructor, argumentList). + return TypedArrayHelper::TypedArrayCreate(thread, thisConstructor, argumentList); +} + +// es11 22.2.4.6 TypedArrayCreate ( constructor, argumentList ) +JSHandle TypedArrayHelper::TypedArrayCreate(JSThread *thread, const JSHandle &constructor, + const JSHandle &argumentList) +{ + // 1. Let newTypedArray be ? Construct(constructor, argumentList). + JSTaggedValue taggedArray = JSFunction::Construct(thread, constructor, argumentList, + JSHandle(thread, JSTaggedValue::Undefined())); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + [[maybe_unused]] JSType type = taggedArray.GetTaggedObject()->GetClass()->GetObjectType(); + if (!taggedArray.IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to construct the Typedarray.", + JSHandle(thread, JSTaggedValue::Exception())); + } + JSHandle taggedArrayHandle(thread, taggedArray); + // 2. Perform ? ValidateTypedArray(newTypedArray). + TypedArrayHelper::ValidateTypedArray(thread, taggedArrayHandle); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + JSHandle newTypedArray(taggedArrayHandle); + // 3. If argumentList is a List of a single Number, then + // a. If newTypedArray.[[ArrayLength]] < argumentList[0], throw a TypeError exception. + if (argumentList->GetLength() == 1) { + if (TypedArrayHelper::GetArrayLength(thread, newTypedArray) < + JSTaggedValue::ToInt32(thread, JSHandle(thread, argumentList->Get(0)))) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the length of newTypedArray is not a correct value.", + JSHandle(thread, JSTaggedValue::Exception())); + } + } + // 4. Return newTypedArray. + return newTypedArray; +} + +// es11 22.2.3.5.1 Runtime Semantics: ValidateTypedArray ( O ) +JSTaggedValue TypedArrayHelper::ValidateTypedArray(JSThread *thread, const JSHandle &value) +{ + // 1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). + // 2. Assert: O has a [[ViewedArrayBuffer]] internal slot. + if (!value->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The O is not a TypedArray.", JSTaggedValue::Exception()); + } + // 3. Let buffer be O.[[ViewedArrayBuffer]]. + // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + JSTaggedValue buffer = TypedArrayHelper::GetViewedArrayBuffer(JSHandle::Cast(value)); + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The ViewedArrayBuffer of O is detached buffer.", + JSTaggedValue::Exception()); + } + // 5. Return buffer. + return buffer; +} + +int32_t TypedArrayHelper::SortCompare(JSThread *thread, const JSHandle &callbackfnHandle, + const JSHandle &buffer, const JSHandle &x, + const JSHandle &y) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 1. Assert: Both Type(x) and Type(y) is Number. + ASSERT(x->IsNumber() && y->IsNumber()); + // 2. If the argument comparefn is not undefined, then + // a. Let v be Call(comparefn, undefined, «x, y»). + // b. ReturnIfAbrupt(v). + // c. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + // d. If v is NaN, return +0. + // e. Return v. + if (!callbackfnHandle->IsUndefined()) { + const array_size_t LEN = 2; + JSHandle msg = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(LEN); + JSHandle thisArgHandle = globalConst->GetHandledUndefined(); + msg->Set(thread, 0, x); + msg->Set(thread, 1, y); + JSTaggedValue callResult = JSFunction::Call(thread, callbackfnHandle, thisArgHandle, msg); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The buffer is detached buffer.", 0); + } + JSHandle testResult(thread, callResult); + JSTaggedNumber v = JSTaggedValue::ToNumber(thread, testResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + double value = v.GetNumber(); + if (std::isnan(value)) { + return +0; + } + return value; + } + // 3. If x and y are both NaN, return +0. + if (NumberHelper::IsNaN(x.GetTaggedValue())) { + if (NumberHelper::IsNaN(y.GetTaggedValue())) { + return +0; + } + // 4. If x is NaN, return 1. + return 1; + } + // 5. If y is NaN, return -1. + if (NumberHelper::IsNaN(y.GetTaggedValue())) { + return -1; + } + ComparisonResult compareResult = JSTaggedValue::Compare(thread, x, y); + // 6. If x < y, return -1. + // 7. If x > y, return 1. + // 8. If x is -0 and y is +0, return -1. + // 9. If x is +0 and y is -0, return 1. + // 10. Return +0. + if (compareResult == ComparisonResult::LESS) { + return -1; + } + if (compareResult == ComparisonResult::GREAT) { + return 1; + } + JSTaggedNumber xNumber = JSTaggedValue::ToNumber(thread, x); + JSTaggedNumber yNumber = JSTaggedValue::ToNumber(thread, y); + double eZeroTemp = -0.0; + auto eZero = JSTaggedNumber(eZeroTemp); + double pZeroTemp = +0.0; + auto pZero = JSTaggedNumber(pZeroTemp); + if (JSTaggedNumber::SameValue(xNumber, eZero) && JSTaggedNumber::SameValue(yNumber, pZero)) { + return -1; + } + if (JSTaggedNumber::SameValue(xNumber, pZero) && JSTaggedNumber::SameValue(yNumber, eZero)) { + return 1; + } + return +0; +} +} // namespace panda::ecmascript::base diff --git a/ecmascript/base/typed_array_helper.h b/ecmascript/base/typed_array_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..1d59abebbed3085789e22350c001b4221585ced4 --- /dev/null +++ b/ecmascript/base/typed_array_helper.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_H +#define PANDA_RUNTIME_ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_dataview.h" +#include +#include + +namespace panda::ecmascript::base { +enum ElementSize : uint8_t { ONE = 1, TWO = 2, FOUR = 4, EIGHT = 8 }; + +class TypedArrayHelper { +public: + static JSTaggedValue TypedArrayConstructor(EcmaRuntimeCallInfo *argv, + const JSHandle &constructorName); + static JSHandle AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm, + const JSHandle &constructorName, + const JSHandle &newTarget); + static JSHandle AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm, + const JSHandle &constructorName, + const JSHandle &newTarget, int32_t length); + static JSHandle TypedArraySpeciesCreate(JSThread *thread, const JSHandle &obj, + const JSHandle &argumentList); + static JSHandle TypedArrayCreate(JSThread *thread, const JSHandle &constructor, + const JSHandle &argumentList); + static JSTaggedValue ValidateTypedArray(JSThread *thread, const JSHandle &value); + inline static DataViewType GetType(const JSHandle &obj); + inline static int32_t GetElementSize(const JSHandle &obj); + inline static DataViewType GetTypeFromName(JSThread *thread, const JSHandle &typeName); + inline static JSHandle GetConstructor(JSThread *thread, const JSHandle &obj); + inline static JSHandle GetConstructorFromName(JSThread *thread, + const JSHandle &typeName); + inline static int32_t GetSizeFromName(JSThread *thread, const JSHandle &typeName); + inline static JSTaggedValue GetViewedArrayBuffer(const JSHandle &obj); + inline static JSHandle GetTypedArrayName(JSThread *thread, const JSHandle &obj); + inline static int32_t GetByteLength(JSThread *thread, const JSHandle &obj); + inline static int32_t GetByteOffset(JSThread *thread, const JSHandle &obj); + inline static int32_t GetArrayLength(JSThread *thread, const JSHandle &obj); + static int32_t SortCompare(JSThread *thread, const JSHandle &callbackfnHandle, + const JSHandle &buffer, const JSHandle &x, + const JSHandle &y); + +private: + static JSTaggedValue CreateFromOrdinaryObject(EcmaRuntimeCallInfo *argv, const JSHandle &obj); + static JSTaggedValue CreateFromTypedArray(EcmaRuntimeCallInfo *argv, const JSHandle &obj, + const JSHandle &constructorName); + static JSTaggedValue CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, const JSHandle &obj, + const JSHandle &constructorName); + static JSHandle AllocateTypedArrayBuffer(JSThread *thread, EcmaVM *ecmaVm, const JSHandle &obj, + double length); + inline static void SetViewedArrayBuffer(JSThread *thread, const JSHandle &obj, + JSTaggedValue viewedArrayBuffer); + inline static void SetTypedArrayName(JSThread *thread, const JSHandle &obj, + const JSHandle &typedArrayName); + inline static void SetByteLength(JSThread *thread, const JSHandle &obj, int32_t byteLength); + inline static void SetByteOffset(JSThread *thread, const JSHandle &obj, int32_t byteOffset); + inline static void SetArrayLength(JSThread *thread, const JSHandle &obj, int32_t arrayLength); +}; +} // namespace panda::ecmascript::base + +#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_H diff --git a/ecmascript/base/utf_helper.cpp b/ecmascript/base/utf_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..25e0995e73e90a9ad62ef737d8dfdddb32f10207 --- /dev/null +++ b/ecmascript/base/utf_helper.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/utf_helper.h" + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +static constexpr int32_t U16_SURROGATE_OFFSET = (0xd800 << 10UL) + 0xdc00 - 0x10000; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define U16_GET_SUPPLEMENTARY(lead, trail) \ + ((static_cast(lead) << 10UL) + static_cast(trail) - U16_SURROGATE_OFFSET) + +namespace panda::ecmascript::base::utf_helper { +uint32_t UTF16Decode(uint16_t lead, uint16_t trail) +{ + ASSERT((lead >= DECODE_LEAD_LOW && lead <= DECODE_LEAD_HIGH) && + (trail >= DECODE_TRAIL_LOW && trail <= DECODE_TRAIL_HIGH)); + uint32_t cp = (lead - DECODE_LEAD_LOW) * DECODE_FIRST_FACTOR + (trail - DECODE_TRAIL_LOW) + DECODE_SECOND_FACTOR; + return cp; +} + +bool IsValidUTF8(const std::vector &data) +{ + uint32_t length = data.size(); + switch (length) { + case UtfLength::ONE: + if (data.at(0) > BIT_MASK_1) { + return false; + } + break; + case UtfLength::TWO: + if ((data.at(0) & BIT_MASK_3) != BIT_MASK_2) { + return false; + } + break; + case UtfLength::THREE: + if ((data.at(0) & BIT_MASK_4) != BIT_MASK_3) { + return false; + } + break; + case UtfLength::FOUR: + if ((data.at(0) & BIT_MASK_5) != BIT_MASK_4) { + return false; + } + break; + default: + UNREACHABLE(); + break; + } + + for (uint32_t i = 1; i < length; i++) { + if ((data.at(i) & BIT_MASK_2) != BIT_MASK_1) { + return false; + } + } + return true; +} + +Utf8Char ConvertUtf16ToUtf8(uint16_t d0, uint16_t d1, bool modify) +{ + // when first utf16 code is in 0xd800-0xdfff and second utf16 code is 0, + // means that is a single code point, it needs to be represented by three UTF8 code. + if (d1 == 0 && d0 >= utf::HI_SURROGATE_MIN && d0 <= utf::LO_SURROGATE_MAX) { + auto ch0 = static_cast(UTF8_3B_FIRST | static_cast(d0 >> UtfOffset::TWELVE)); + auto ch1 = static_cast(UTF8_3B_SECOND | (static_cast(d0 >> UtfOffset::SIX) & utf::MASK_6BIT)); + auto ch2 = static_cast(UTF8_3B_THIRD | (d0 & utf::MASK_6BIT)); + return {UtfLength::THREE, {ch0, ch1, ch2}}; + } + + if (d0 == 0) { + if (modify) { + // special case for \u0000 ==> C080 - 1100'0000 1000'0000 + return {UtfLength::TWO, {UTF8_2B_FIRST, UTF8_2B_SECOND}}; + } + // For print string, just skip '\u0000' + return {0, {0x00U}}; + } + if (d0 <= UTF8_1B_MAX) { + return {UtfLength::ONE, {static_cast(d0)}}; + } + if (d0 <= UTF8_2B_MAX) { + auto ch0 = static_cast(UTF8_2B_FIRST | static_cast(d0 >> UtfOffset::SIX)); + auto ch1 = static_cast(UTF8_2B_SECOND | (d0 & utf::MASK_6BIT)); + return {UtfLength::TWO, {ch0, ch1}}; + } + if (d0 < utf::HI_SURROGATE_MIN || d0 > utf::HI_SURROGATE_MAX) { + auto ch0 = static_cast(UTF8_3B_FIRST | static_cast(d0 >> UtfOffset::TWELVE)); + auto ch1 = static_cast(UTF8_3B_SECOND | (static_cast(d0 >> UtfOffset::SIX) & utf::MASK_6BIT)); + auto ch2 = static_cast(UTF8_3B_THIRD | (d0 & utf::MASK_6BIT)); + return {UtfLength::THREE, {ch0, ch1, ch2}}; + } + if (d1 < utf::LO_SURROGATE_MIN || d1 > utf::LO_SURROGATE_MAX) { + // Bad sequence + UNREACHABLE(); + } + + uint32_t codePoint = CombineTwoU16(d0, d1); + + auto ch0 = static_cast((codePoint >> UtfOffset::EIGHTEEN) | UTF8_4B_FIRST); + auto ch1 = static_cast(((codePoint >> UtfOffset::TWELVE) & utf::MASK_6BIT) | utf::MASK1); + auto ch2 = static_cast(((codePoint >> UtfOffset::SIX) & utf::MASK_6BIT) | utf::MASK1); + auto ch3 = static_cast((codePoint & utf::MASK_6BIT) | utf::MASK1); + return {UtfLength::FOUR, {ch0, ch1, ch2, ch3}}; +} + +size_t Utf16ToUtf8Size(const uint16_t *utf16, uint32_t length, bool modify) +{ + size_t res = 1; // zero byte + // when utf16 data length is only 1 and code in 0xd800-0xdfff, + // means that is a single code point, it needs to be represented by three UTF8 code. + if (length == 1 && utf16[0] >= utf::HI_SURROGATE_MIN && // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + utf16[0] <= utf::LO_SURROGATE_MAX) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + res += UtfLength::THREE; + return res; + } + + for (uint32_t i = 0; i < length; ++i) { + if (utf16[i] == 0) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (modify) { + res += UtfLength::TWO; // special case for U+0000 => C0 80 + } + } else if (utf16[i] <= UTF8_1B_MAX) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + res += 1; + } else if (utf16[i] <= UTF8_2B_MAX) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + res += UtfLength::TWO; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (utf16[i] < utf::HI_SURROGATE_MIN || utf16[i] > utf::HI_SURROGATE_MAX) { + res += UtfLength::THREE; + } else { + res += UtfLength::FOUR; + ++i; + } + } + return res; +} + +size_t ConvertRegionUtf16ToUtf8(const uint16_t *utf16In, uint8_t *utf8Out, size_t utf16Len, size_t utf8Len, + size_t start, bool modify) +{ + size_t utf8Pos = 0; + if (utf16In == nullptr || utf8Out == nullptr || utf8Len == 0) { + return 0; + } + size_t end = start + utf16Len; + for (size_t i = start; i < end; ++i) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint16_t next16Code = 0; + if ((i + 1) != end && utf::IsAvailableNextUtf16Code(utf16In[i + 1])) { + next16Code = utf16In[i + 1]; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + Utf8Char ch = ConvertUtf16ToUtf8(utf16In[i], next16Code, modify); + if (utf8Pos + ch.n > utf8Len) { + break; + } + for (size_t c = 0; c < ch.n; ++c) { + utf8Out[utf8Pos++] = ch.ch[c]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + if (ch.n == UtfLength::FOUR) { // Two UTF-16 chars are used + ++i; + } + } + return utf8Pos; +} + +std::pair ConvertUtf8ToUtf16Pair(const uint8_t *data, bool combine) +{ + uint8_t d0 = data[0]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if ((d0 & utf::MASK1) == 0) { + return {d0, 1}; + } + + uint8_t d1 = data[1]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if ((d0 & utf::MASK2) == 0) { + return {((d0 & utf::MASK_5BIT) << utf::DATA_WIDTH) | (d1 & utf::MASK_6BIT), UtfLength::TWO}; + } + + uint8_t d2 = data[UtfLength::TWO]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if ((d0 & utf::MASK3) == 0) { + return {((d0 & utf::MASK_4BIT) << UtfOffset::TWELVE) | ((d1 & utf::MASK_6BIT) << utf::DATA_WIDTH) | + (d2 & utf::MASK_6BIT), + UtfLength::THREE}; + } + + uint8_t d3 = data[UtfLength::THREE]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint32_t codePoint = ((d0 & utf::MASK_4BIT) << UtfOffset::EIGHTEEN) | ((d1 & utf::MASK_6BIT) << UtfOffset::TWELVE) | + ((d2 & utf::MASK_6BIT) << utf::DATA_WIDTH) | (d3 & utf::MASK_6BIT); + + uint32_t pair = 0; + if (combine) { + uint32_t lead = ((codePoint >> (utf::PAIR_ELEMENT_WIDTH - utf::DATA_WIDTH)) + utf::U16_LEAD); + uint32_t tail = ((codePoint & utf::MASK_10BIT) + utf::U16_TAIL) & utf::MASK_16BIT; + pair = U16_GET_SUPPLEMENTARY(lead, tail); // NOLINTNEXTLINE(hicpp-signed-bitwise) + } else { + pair |= ((codePoint >> (utf::PAIR_ELEMENT_WIDTH - utf::DATA_WIDTH)) + utf::U16_LEAD) << utf::PAIR_ELEMENT_WIDTH; + pair |= ((codePoint & utf::MASK_10BIT) + utf::U16_TAIL) & utf::MASK_16BIT; + } + + return {pair, UtfLength::FOUR}; +} + +size_t Utf8ToUtf16Size(const uint8_t *utf8) +{ + size_t res = 0; + while (*utf8 != '\0') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto [pair, nbytes] = ConvertUtf8ToUtf16Pair(utf8); + res += pair > 0xffff ? UtfLength::TWO : UtfLength::ONE; // NOLINT(readability-magic-numbers) + utf8 += nbytes; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + return res; +} + +size_t ConvertRegionUtf8ToUtf16(const uint8_t *utf8In, uint16_t *utf16Out, size_t utf16Len, size_t start) +{ + ASSERT(utf16Out != nullptr); + size_t outPos = 0; + while (*utf8In != '\0') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto [pair, nbytes] = ConvertUtf8ToUtf16Pair(utf8In); + auto [pHi, pLo] = utf::SplitUtf16Pair(pair); + + utf8In += nbytes; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (start > 0) { + start -= nbytes; + continue; + } + + if (pHi != 0) { + if (outPos >= utf16Len - 1) { // check for place for two uint16 + break; + } + outPos++; + *utf16Out++ = pHi; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + if (outPos >= utf16Len) { + break; + } + outPos++; + *utf16Out++ = pLo; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (outPos >= utf16Len) { + break; + } + } + + return outPos; +} +} // namespace panda::ecmascript::base::utf_helper diff --git a/ecmascript/base/utf_helper.h b/ecmascript/base/utf_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..6d4dce08d40610ede3090e3791e9cfa8c707eab9 --- /dev/null +++ b/ecmascript/base/utf_helper.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BASE_UTF_HELPER_H +#define PANDA_RUNTIME_ECMASCRIPT_BASE_UTF_HELPER_H + +#include +#include + +#include "libpandabase/utils/utf.h" + +namespace panda::ecmascript::base::utf_helper { +static constexpr uint16_t DECODE_LEAD_LOW = 0xD800; +static constexpr uint16_t DECODE_LEAD_HIGH = 0xDBFF; +static constexpr uint16_t DECODE_TRAIL_LOW = 0xDC00; +static constexpr uint16_t DECODE_TRAIL_HIGH = 0xDFFF; +static constexpr uint32_t DECODE_FIRST_FACTOR = 0x400; +static constexpr uint32_t DECODE_SECOND_FACTOR = 0x10000; + +static constexpr uint8_t BIT_MASK_1 = 0x80; +static constexpr uint8_t BIT_MASK_2 = 0xC0; +static constexpr uint8_t BIT_MASK_3 = 0xE0; +static constexpr uint8_t BIT_MASK_4 = 0xF0; +static constexpr uint8_t BIT_MASK_5 = 0xF8; + +static constexpr uint8_t UTF8_1B_MAX = 0x7f; + +static constexpr uint16_t UTF8_2B_MAX = 0x7ff; +static constexpr uint8_t UTF8_2B_FIRST = 0xc0; +static constexpr uint8_t UTF8_2B_SECOND = 0x80; + +static constexpr uint8_t UTF8_3B_FIRST = 0xe0; +static constexpr uint8_t UTF8_3B_SECOND = 0x80; +static constexpr uint8_t UTF8_3B_THIRD = 0x80; + +static constexpr uint8_t UTF8_4B_FIRST = 0xf0; + +enum UtfLength : uint8_t { ONE = 1, TWO = 2, THREE = 3, FOUR = 4 }; +enum UtfOffset : uint8_t { SIX = 6, TEN = 10, TWELVE = 12, EIGHTEEN = 18 }; + +static constexpr size_t MAX_BYTES = 4; +struct Utf8Char { + size_t n; + std::array ch; +}; + +uint32_t UTF16Decode(uint16_t lead, uint16_t trail); + +bool IsValidUTF8(const std::vector &data); + +Utf8Char ConvertUtf16ToUtf8(uint16_t d0, uint16_t d1, bool modify); + +size_t Utf16ToUtf8Size(const uint16_t *utf16, uint32_t length, bool modify = true); + +size_t ConvertRegionUtf16ToUtf8(const uint16_t *utf16In, uint8_t *utf8Out, size_t utf16Len, size_t utf8Len, + size_t start, bool modify = true); + +std::pair ConvertUtf8ToUtf16Pair(const uint8_t *data, bool combine = false); + +size_t Utf8ToUtf16Size(const uint8_t *utf8); + +size_t ConvertRegionUtf8ToUtf16(const uint8_t *utf8In, uint16_t *utf16Out, size_t utf16Len, size_t start); + +static inline uint32_t CombineTwoU16(uint16_t d0, uint16_t d1) +{ + uint32_t codePoint = d0 - utf::HI_SURROGATE_MIN; + codePoint <<= UtfOffset::TEN; + codePoint |= d1 - utf::LO_SURROGATE_MIN; + codePoint += utf::LO_SUPPLEMENTS_MIN; + return codePoint; +} +} // namespace panda::ecmascript::base::utf_helper + +#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_UTF_HELPER_H \ No newline at end of file diff --git a/ecmascript/builtins.cpp b/ecmascript/builtins.cpp new file mode 100644 index 0000000000000000000000000000000000000000..18e8ec274be157d69266097fd852fae324263b3d --- /dev/null +++ b/ecmascript/builtins.cpp @@ -0,0 +1,2401 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/error_type.h" +#include "ecmascript/base/number_helper.h" +#include "ecmascript/builtins.h" +#include "ecmascript/builtins/builtins_array.h" +#include "ecmascript/builtins/builtins_arraybuffer.h" +#include "ecmascript/builtins/builtins_async_function.h" +#include "ecmascript/builtins/builtins_boolean.h" +#include "ecmascript/builtins/builtins_dataview.h" +#include "ecmascript/builtins/builtins_date.h" +#include "ecmascript/builtins/builtins_errors.h" +#include "ecmascript/builtins/builtins_function.h" +#include "ecmascript/builtins/builtins_generator.h" +#include "ecmascript/builtins/builtins_global.h" +#include "ecmascript/builtins/builtins_iterator.h" +#include "ecmascript/builtins/builtins_json.h" +#include "ecmascript/builtins/builtins_map.h" +#include "ecmascript/builtins/builtins_math.h" +#include "ecmascript/builtins/builtins_number.h" +#include "ecmascript/builtins/builtins_object.h" +#include "ecmascript/builtins/builtins_promise.h" +#include "ecmascript/builtins/builtins_promise_handler.h" +#include "ecmascript/builtins/builtins_promise_job.h" +#include "ecmascript/builtins/builtins_proxy.h" +#include "ecmascript/builtins/builtins_reflect.h" +#include "ecmascript/builtins/builtins_regexp.h" +#include "ecmascript/builtins/builtins_set.h" +#include "ecmascript/builtins/builtins_string.h" +#include "ecmascript/builtins/builtins_string_iterator.h" +#include "ecmascript/builtins/builtins_symbol.h" +#include "ecmascript/builtins/builtins_typedarray.h" +#include "ecmascript/builtins/builtins_weak_map.h" +#include "ecmascript/builtins/builtins_weak_set.h" +#include "ecmascript/ecma_runtime_call_info.h" + +#include "ecmascript/js_array.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_async_function.h" +#include "ecmascript/js_dataview.h" +#include "ecmascript/js_float32_array.h" +#include "ecmascript/js_float64_array.h" +#include "ecmascript/js_for_in_iterator.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_int16_array.h" +#include "ecmascript/js_int32_array.h" +#include "ecmascript/js_int8_array.h" +#include "ecmascript/js_map.h" +#include "ecmascript/js_map_iterator.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_regexp.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/js_string_iterator.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_typed_array.h" +#include "ecmascript/js_uint16_array.h" +#include "ecmascript/js_uint32_array.h" +#include "ecmascript/js_uint8_array.h" +#include "ecmascript/js_uint8_clamped_array.h" +#include "ecmascript/js_weak_container.h" +#include "ecmascript/mem/mem.h" +#include "ecmascript/object_factory.h" +#include "ohos/init_data.h" + +namespace panda::ecmascript { +using Number = builtins::BuiltinsNumber; +using Object = builtins::BuiltinsObject; +using Date = builtins::BuiltinsDate; +using Symbol = builtins::BuiltinsSymbol; +using Boolean = builtins::BuiltinsBoolean; +using BuiltinsMap = builtins::BuiltinsMap; +using BuiltinsSet = builtins::BuiltinsSet; +using BuiltinsWeakMap = builtins::BuiltinsWeakMap; +using BuiltinsWeakSet = builtins::BuiltinsWeakSet; +using BuiltinsArray = builtins::BuiltinsArray; +using BuiltinsTypedArray = builtins::BuiltinsTypedArray; +using BuiltinsIterator = builtins::BuiltinsIterator; + +using Error = builtins::BuiltinsError; +using RangeError = builtins::BuiltinsRangeError; +using ReferenceError = builtins::BuiltinsReferenceError; +using TypeError = builtins::BuiltinsTypeError; +using URIError = builtins::BuiltinsURIError; +using SyntaxError = builtins::BuiltinsSyntaxError; +using EvalError = builtins::BuiltinsEvalError; +using ErrorType = base::ErrorType; +using Global = builtins::BuiltinsGlobal; +using BuiltinsString = builtins::BuiltinsString; +using StringIterator = builtins::BuiltinsStringIterator; +using RegExp = builtins::BuiltinsRegExp; +using Function = builtins::BuiltinsFunction; +using Math = builtins::BuiltinsMath; +using ArrayBuffer = builtins::BuiltinsArrayBuffer; +using Json = builtins::BuiltinsJson; +using Proxy = builtins::BuiltinsProxy; +using Reflect = builtins::BuiltinsReflect; +using AsyncFunction = builtins::BuiltinsAsyncFunction; +using GeneratorObject = builtins::BuiltinsGenerator; +using Promise = builtins::BuiltinsPromise; +using BuiltinsPromiseHandler = builtins::BuiltinsPromiseHandler; +using BuiltinsPromiseJob = builtins::BuiltinsPromiseJob; +using ErrorType = base::ErrorType; +using DataView = builtins::BuiltinsDataView; + +void Builtins::Initialize(const JSHandle &env, JSThread *thread) +{ + thread_ = thread; + vm_ = thread->GetEcmaVM(); + factory_ = vm_->GetFactory(); + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle nullHandle(thread, JSTaggedValue::Null()); + + // Object.prototype[dynclass] + JSHandle objPrototypeDynclass = factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, nullHandle); + + // Object.prototype + JSHandle objFuncPrototype = factory_->NewJSObject(objPrototypeDynclass); + JSHandle objFuncPrototypeVal(objFuncPrototype); + + // Object.prototype_or_dynclass + JSHandle objFuncDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, objFuncPrototypeVal); + + // GLobalObject.prototype_or_dynclass + JSHandle globalObjFuncDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_GLOBAL_OBJECT, objFuncPrototypeVal); + globalObjFuncDynclass->SetIsDictionaryMode(true); + // Function.prototype_or_dynclass + JSHandle emptyFuncDynclass( + factory_->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, objFuncPrototypeVal)); + + // PrimitiveRef.prototype_or_dynclass + JSHandle primRefObjDynclass = + factory_->NewEcmaDynClass(JSPrimitiveRef::SIZE, JSType::JS_PRIMITIVE_REF, objFuncPrototypeVal); + + // init global object + JSHandle globalObject = factory_->NewNonMovableJSObject(globalObjFuncDynclass); + JSHandle newGlobalDynclass = JSHClass::Clone(thread_, globalObjFuncDynclass); + globalObject->SetClass(newGlobalDynclass); + env->SetJSGlobalObject(thread_, globalObject); + + // initialize Function, forbidden change order + InitializeFunction(env, emptyFuncDynclass); + + JSHandle objFuncInstancePrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle objFuncInstancePrototypeValue(objFuncInstancePrototype); + JSHandle asyncFuncClass = factory_->CreateFunctionClass( + FunctionKind::ASYNC_FUNCTION, JSAsyncFunction::SIZE, JSType::JS_ASYNC_FUNCTION, objFuncInstancePrototypeValue); + env->SetAsyncFunctionClass(thread_, asyncFuncClass); + + JSHandle asyncAwaitStatusFuncClass = + factory_->CreateFunctionClass(FunctionKind::NORMAL_FUNCTION, JSAsyncAwaitStatusFunction::SIZE, + JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION, env->GetFunctionPrototype()); + env->SetAsyncAwaitStatusFunctionClass(thread_, asyncAwaitStatusFuncClass); + + JSHandle promiseReactionFuncClass = factory_->NewEcmaDynClass( + JSPromiseReactionsFunction::SIZE, JSType::JS_PROMISE_REACTIONS_FUNCTION, env->GetFunctionPrototype()); + promiseReactionFuncClass->SetCallable(true); + promiseReactionFuncClass->SetExtensible(true); + env->SetPromiseReactionFunctionClass(thread_, promiseReactionFuncClass); + + JSHandle promiseExecutorFuncClass = factory_->NewEcmaDynClass( + JSPromiseExecutorFunction::SIZE, JSType::JS_PROMISE_EXECUTOR_FUNCTION, env->GetFunctionPrototype()); + promiseExecutorFuncClass->SetCallable(true); + promiseExecutorFuncClass->SetExtensible(true); + env->SetPromiseExecutorFunctionClass(thread_, promiseExecutorFuncClass); + + JSHandle promiseAllResolveElementFunctionClass = + factory_->NewEcmaDynClass(JSPromiseAllResolveElementFunction::SIZE, + JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION, env->GetFunctionPrototype()); + promiseAllResolveElementFunctionClass->SetCallable(true); + promiseAllResolveElementFunctionClass->SetExtensible(true); + env->SetPromiseAllResolveElementFunctionClass(thread_, promiseAllResolveElementFunctionClass); + + JSHandle proxyRevocFuncClass = factory_->NewEcmaDynClass( + JSProxyRevocFunction::SIZE, JSType::JS_PROXY_REVOC_FUNCTION, env->GetFunctionPrototype()); + proxyRevocFuncClass->SetCallable(true); + proxyRevocFuncClass->SetExtensible(true); + env->SetProxyRevocFunctionClass(thread_, proxyRevocFuncClass); + + // Object = new Function() + JSHandle objectFunction( + NewBuiltinConstructor(env, objFuncPrototype, Object::ObjectConstructor, "Object", FunctionLength::ONE)); + objectFunction.GetObject()->SetBuiltinsCtorMode(); + objectFunction.GetObject()->SetFunctionPrototype(thread_, objFuncDynclass.GetTaggedValue()); + // initialize object method. + env->SetObjectFunction(thread_, objectFunction); + env->SetObjectFunctionPrototype(thread_, objFuncPrototype); + + JSHandle functionClass = factory_->CreateFunctionClass(FunctionKind::BASE_CONSTRUCTOR, JSFunction::SIZE, + JSType::JS_FUNCTION, env->GetFunctionPrototype()); + env->SetFunctionClassWithProto(thread_, functionClass); + functionClass = factory_->CreateFunctionClass(FunctionKind::NORMAL_FUNCTION, JSFunction::SIZE, JSType::JS_FUNCTION, + env->GetFunctionPrototype()); + env->SetFunctionClassWithoutProto(thread_, functionClass); + functionClass = factory_->CreateFunctionClass(FunctionKind::CLASS_CONSTRUCTOR, JSFunction::SIZE, + JSType::JS_FUNCTION, env->GetFunctionPrototype()); + env->SetFunctionClassWithoutName(thread_, functionClass); + + if (env == vm_->GetGlobalEnv()) { + InitializeAllTypeError(env, objFuncDynclass); + InitializeSymbol(env, primRefObjDynclass); + } else { + // error and symbol need to be shared when initialize realm + InitializeAllTypeErrorWithRealm(env); + InitializeSymbolWithRealm(env, primRefObjDynclass); + } + + InitializeNumber(env, globalObject, primRefObjDynclass); + InitializeDate(env, objFuncDynclass); + InitializeObject(env, objFuncPrototype, objectFunction); + InitializeBoolean(env, primRefObjDynclass); + + InitializeRegExp(env); + InitializeSet(env, objFuncDynclass); + InitializeMap(env, objFuncDynclass); + InitializeWeakMap(env, objFuncDynclass); + InitializeWeakSet(env, objFuncDynclass); + InitializeArray(env, objFuncPrototypeVal); + InitializeTypedArray(env, objFuncDynclass); + InitializeString(env, primRefObjDynclass); + InitializeArrayBuffer(env, objFuncDynclass); + InitializeDataView(env, objFuncDynclass); + InitializeJSNativeObject(env); + + JSHandle argumentsDynclass = factory_->CreateJSArguments(); + env->SetArgumentsClass(thread_, argumentsDynclass); + SetArgumentsSharedAccessor(env); + + InitializeGlobalObject(env, globalObject); + InitializeMath(env, objFuncPrototypeVal); + InitializeJson(env, objFuncPrototypeVal); + InitializeIterator(env, objFuncDynclass); + InitializeProxy(env); + InitializeReflect(env, objFuncPrototypeVal); + InitializeAsyncFunction(env, objFuncDynclass); + InitializeGenerator(env, objFuncDynclass); + InitializeGeneratorFunction(env, objFuncDynclass); + InitializePromise(env, objFuncDynclass); + InitializePromiseJob(env); + + SetHwIcuDirectory(); + + JSHandle generatorFuncClass = + factory_->CreateFunctionClass(FunctionKind::GENERATOR_FUNCTION, JSFunction::SIZE, JSType::JS_GENERATOR_FUNCTION, + env->GetGeneratorFunctionPrototype()); + env->SetGeneratorFunctionClass(thread_, generatorFuncClass); + env->SetObjectFunctionPrototypeClass(thread_, JSTaggedValue(objFuncPrototype->GetClass())); + thread_->ResetGuardians(); +} + +void Builtins::InitializeGlobalObject(const JSHandle &env, const JSHandle &globalObject) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + + // Global object test + SetFunction(env, globalObject, "print", Global::PrintEntrypoint, 0); +#ifdef PANDA_ECMASCRIPT_ENABLE_RUNTIME_STAT + SetFunction(env, globalObject, "startRuntimeStat", Global::StartRuntimeStat, 0); + SetFunction(env, globalObject, "stopRuntimeStat", Global::StopRuntimeStat, 0); +#endif + + // Global object function + SetFunction(env, globalObject, "eval", Global::NotSupportEval, FunctionLength::ONE); + SetFunction(env, globalObject, "isFinite", Global::IsFinite, FunctionLength::ONE); + SetFunction(env, globalObject, "isNaN", Global::IsNaN, FunctionLength::ONE); + SetFunction(env, globalObject, "decodeURI", Global::DecodeURI, FunctionLength::ONE); + SetFunction(env, globalObject, "encodeURI", Global::EncodeURI, FunctionLength::ONE); + SetFunction(env, globalObject, "decodeURIComponent", Global::DecodeURIComponent, FunctionLength::ONE); + SetFunction(env, globalObject, "encodeURIComponent", Global::EncodeURIComponent, FunctionLength::ONE); + + // Global object property + SetGlobalThis(globalObject, "globalThis", JSHandle::Cast(globalObject)); + SetConstant(globalObject, "Infinity", JSTaggedValue(base::POSITIVE_INFINITY)); + SetConstant(globalObject, "NaN", JSTaggedValue(base::NAN_VALUE)); + SetConstant(globalObject, "undefined", JSTaggedValue::Undefined()); +} + +void Builtins::InitializeFunction(const JSHandle &env, const JSHandle &emptyFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Initialize Function.prototype + JSMethod *invokeSelf = + vm_->GetMethodForNativeFunction(reinterpret_cast(Function::FunctionPrototypeInvokeSelf)); + JSHandle funcFuncPrototype = factory_->NewJSFunctionByDynClass(invokeSelf, emptyFuncDynclass); + // ecma 19.2.3 The value of the name property of the Function prototype object is the empty String. + JSHandle emptyString(thread_->GlobalConstants()->GetHandledEmptyString()); + JSHandle undefinedString(thread_, JSTaggedValue::Undefined()); + JSFunction::SetFunctionName(thread_, JSHandle(funcFuncPrototype), emptyString, undefinedString); + // ecma 19.2.3 The value of the length property of the Function prototype object is 0. + JSFunction::SetFunctionLength(thread_, funcFuncPrototype, JSTaggedValue(FunctionLength::ZERO)); + + JSHandle funcFuncPrototypeValue(funcFuncPrototype); + // Function.prototype_or_dynclass + JSHandle funcFuncIntanceDynclass = + factory_->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, funcFuncPrototypeValue); + funcFuncIntanceDynclass->SetConstructor(true); + JSHandle function = JSHandle::Cast(factory_->NewJSObject(funcFuncIntanceDynclass)); + function->SetBuiltinsCtorMode(); + + // Function = new Function() (forbidden use NewBuiltinConstructor) + JSMethod *ctor = vm_->GetMethodForNativeFunction(reinterpret_cast(Function::FunctionConstructor)); + JSHandle funcFunc = + factory_->NewJSFunctionByDynClass(ctor, funcFuncIntanceDynclass, FunctionKind::BUILTIN_CONSTRUCTOR); + + auto funcFuncPrototypeObj = JSHandle(funcFuncPrototype); + InitializeCtor(env, funcFuncPrototypeObj, funcFunc, "Function", FunctionLength::ONE); + + funcFunc->SetFunctionPrototype(thread_, funcFuncIntanceDynclass.GetTaggedValue()); + env->SetFunctionFunction(thread_, funcFunc); + env->SetFunctionPrototype(thread_, funcFuncPrototype); + + JSHandle normalFuncClass = + factory_->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, env->GetFunctionPrototype()); + env->SetNormalFunctionClass(thread_, normalFuncClass); + + JSHandle constructorFunctionClass = + factory_->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, env->GetFunctionPrototype()); + constructorFunctionClass->SetConstructor(true); + JSHandle functionConstructor = + JSHandle::Cast(factory_->NewJSObject(constructorFunctionClass)); + functionConstructor->SetBuiltinsCtorMode(); + env->SetConstructorFunctionClass(thread_, constructorFunctionClass); + + StrictModeForbiddenAccessCallerArguments(env, funcFuncPrototypeObj); + + // Function.prototype method + // 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) + SetFunction(env, funcFuncPrototypeObj, "apply", Function::FunctionPrototypeApply, FunctionLength::TWO); + // 19.2.3.2 Function.prototype.bind ( thisArg , ...args) + SetFunction(env, funcFuncPrototypeObj, "bind", Function::FunctionPrototypeBind, FunctionLength::ONE); + // 19.2.3.3 Function.prototype.call (thisArg , ...args) + SetFunction(env, funcFuncPrototypeObj, "call", Function::FunctionPrototypeCall, FunctionLength::ONE); + // 19.2.3.5 Function.prototype.toString ( ) + SetFunction(env, funcFuncPrototypeObj, thread_->GlobalConstants()->GetHandledToStringString(), + Function::FunctionPrototypeToString, FunctionLength::ZERO); +} + +void Builtins::InitializeObject(const JSHandle &env, const JSHandle &objFuncPrototype, + const JSHandle &objFunc) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Object method. + // 19.1.2.1Object.assign ( target, ...sources ) + SetFunction(env, objFunc, "assign", Object::Assign, FunctionLength::TWO); + // 19.1.2.2Object.create ( O [ , Properties ] ) + SetFunction(env, objFunc, "create", Object::Create, FunctionLength::TWO); + // 19.1.2.3Object.defineProperties ( O, Properties ) + SetFunction(env, objFunc, "defineProperties", Object::DefineProperties, FunctionLength::TWO); + // 19.1.2.4Object.defineProperty ( O, P, Attributes ) + SetFunction(env, objFunc, "defineProperty", Object::DefineProperty, FunctionLength::THREE); + // 19.1.2.5Object.freeze ( O ) + SetFunction(env, objFunc, "freeze", Object::Freeze, FunctionLength::ONE); + // 19.1.2.6Object.getOwnPropertyDescriptor ( O, P ) + SetFunction(env, objFunc, "getOwnPropertyDescriptor", Object::GetOwnPropertyDesciptor, FunctionLength::TWO); + // 19.1.2.7Object.getOwnPropertyNames ( O ) + SetFunction(env, objFunc, "getOwnPropertyNames", Object::GetOwnPropertyNames, FunctionLength::ONE); + // 19.1.2.8Object.getOwnPropertySymbols ( O ) + SetFunction(env, objFunc, "getOwnPropertySymbols", Object::GetOwnPropertySymbols, FunctionLength::ONE); + // 19.1.2.9Object.getPrototypeOf ( O ) + SetFunction(env, objFunc, "getPrototypeOf", Object::GetPrototypeOf, FunctionLength::ONE); + // 19.1.2.10Object.is ( value1, value2 ) + SetFunction(env, objFunc, "is", Object::Is, 2); + // 19.1.2.11Object.isExtensible ( O ) + SetFunction(env, objFunc, "isExtensible", Object::IsExtensible, FunctionLength::ONE); + // 19.1.2.12Object.isFrozen ( O ) + SetFunction(env, objFunc, "isFrozen", Object::IsFrozen, FunctionLength::ONE); + // 19.1.2.13Object.isSealed ( O ) + SetFunction(env, objFunc, "isSealed", Object::IsSealed, FunctionLength::ONE); + // 19.1.2.14 Object.keys(O) + SetFunction(env, objFunc, "keys", Object::Keys, FunctionLength::ONE); + // 19.1.2.15 Object.preventExtensions(O) + SetFunction(env, objFunc, "preventExtensions", Object::PreventExtensions, FunctionLength::ONE); + // 19.1.2.17 Object.seal(O) + SetFunction(env, objFunc, "seal", Object::Seal, FunctionLength::ONE); + // 19.1.2.18 Object.setPrototypeOf(O, proto) + SetFunction(env, objFunc, "setPrototypeOf", Object::SetPrototypeOf, FunctionLength::TWO); + // 20.1.2.5 Object.entries ( O ) + SetFunction(env, objFunc, "entries", Object::Entries, FunctionLength::ONE); + + // Object.property method + // 19.1.3.2 Object.prototype.hasOwnProperty(V) + SetFunction(env, objFuncPrototype, "hasOwnProperty", Object::HasOwnProperty, FunctionLength::ONE); + // 19.1.3.3 Object.prototype.isPrototypeOf(V) + SetFunction(env, objFuncPrototype, "isPrototypeOf", Object::IsPrototypeOf, FunctionLength::ONE); + // 19.1.3.4 Object.prototype.propertyIsEnumerable(V) + SetFunction(env, objFuncPrototype, "propertyIsEnumerable", Object::PropertyIsEnumerable, FunctionLength::ONE); + // 19.1.3.5 Object.prototype.toLocaleString([reserved1[, reserved2]]) + SetFunction(env, objFuncPrototype, "toLocaleString", Object::ToLocaleString, FunctionLength::ZERO); + // 19.1.3.6 Object.prototype.toString() + SetFunction(env, objFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Object::ToString, + FunctionLength::ZERO); + // 19.1.3.7 Object.prototype.valueOf() + SetFunction(env, objFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), Object::ValueOf, + FunctionLength::ZERO); + + SetFunction(env, objFuncPrototype, "createRealm", Object::CreateRealm, FunctionLength::ZERO); + + // B.2.2.1 Object.prototype.__proto__ + JSHandle protoKey(factory_->NewFromString("__proto__")); + JSHandle protoGetter = CreateGetter(env, Object::ProtoGetter, "__proto__", FunctionLength::ZERO); + JSHandle protoSetter = CreateSetter(env, Object::ProtoSetter, "__proto__", FunctionLength::ONE); + SetAccessor(objFuncPrototype, protoKey, protoGetter, protoSetter); +} + +void Builtins::InitializeSymbol(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // Symbol.prototype + JSHandle symbolFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle symbolFuncPrototypeValue(symbolFuncPrototype); + + // Symbol.prototype_or_dynclass + JSHandle symbolFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSPrimitiveRef::SIZE, JSType::JS_PRIMITIVE_REF, symbolFuncPrototypeValue); + + // Symbol = new Function() + JSHandle symbolFunction( + NewBuiltinConstructor(env, symbolFuncPrototype, Symbol::SymbolConstructor, "Symbol", FunctionLength::ZERO)); + JSHandle(symbolFunction)->SetFunctionPrototype(thread_, symbolFuncInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor descriptor(thread_, JSHandle::Cast(symbolFunction), true, false, true); + JSObject::DefineOwnProperty(thread_, symbolFuncPrototype, constructorKey, descriptor); + + SetFunction(env, symbolFunction, "for", Symbol::For, FunctionLength::ONE); + SetFunction(env, symbolFunction, "keyFor", Symbol::KeyFor, FunctionLength::ONE); + + // Symbol attribute + JSHandle hasInstanceSymbol(factory_->NewWellKnownSymbolWithChar("Symbol.hasInstance")); + SetNoneAttributeProperty(symbolFunction, "hasInstance", hasInstanceSymbol); + JSHandle isConcatSpreadableSymbol(factory_->NewWellKnownSymbolWithChar("Symbol.isConcatSpreadable")); + SetNoneAttributeProperty(symbolFunction, "isConcatSpreadable", isConcatSpreadableSymbol); + JSHandle toStringTagSymbol(factory_->NewWellKnownSymbolWithChar("Symbol.toStringTag")); + SetNoneAttributeProperty(symbolFunction, "toStringTag", toStringTagSymbol); + JSHandle iteratorSymbol(factory_->NewPublicSymbolWithChar("Symbol.iterator")); + SetNoneAttributeProperty(symbolFunction, "iterator", iteratorSymbol); + JSHandle matchSymbol(factory_->NewPublicSymbolWithChar("Symbol.match")); + SetNoneAttributeProperty(symbolFunction, "match", matchSymbol); + JSHandle replaceSymbol(factory_->NewPublicSymbolWithChar("Symbol.replace")); + SetNoneAttributeProperty(symbolFunction, "replace", replaceSymbol); + JSHandle searchSymbol(factory_->NewPublicSymbolWithChar("Symbol.search")); + SetNoneAttributeProperty(symbolFunction, "search", searchSymbol); + JSHandle speciesSymbol(factory_->NewPublicSymbolWithChar("Symbol.species")); + SetNoneAttributeProperty(symbolFunction, "species", speciesSymbol); + JSHandle splitSymbol(factory_->NewPublicSymbolWithChar("Symbol.split")); + SetNoneAttributeProperty(symbolFunction, "split", splitSymbol); + JSHandle toPrimitiveSymbol(factory_->NewPublicSymbolWithChar("Symbol.toPrimitive")); + SetNoneAttributeProperty(symbolFunction, "toPrimitive", toPrimitiveSymbol); + JSHandle unscopablesSymbol(factory_->NewPublicSymbolWithChar("Symbol.unscopables")); + SetNoneAttributeProperty(symbolFunction, "unscopables", unscopablesSymbol); + + // symbol.prototype.description + PropertyDescriptor descriptionDesc(thread_); + JSHandle getterKey(factory_->NewFromString("description")); + JSHandle getter(factory_->NewJSFunction(env, reinterpret_cast(Symbol::DescriptionGetter))); + SetGetter(symbolFuncPrototype, getterKey, getter); + + // Setup symbol.prototype[@@toPrimitive] + SetFunctionAtSymbol( + env, symbolFuncPrototype, toPrimitiveSymbol, "[Symbol.toPrimitive]", Symbol::ToPrimitive, FunctionLength::ONE); + // install the Symbol.prototype methods + SetFunction(env, symbolFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Symbol::ToString, + FunctionLength::ZERO); + SetFunction(env, symbolFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), Symbol::ValueOf, + FunctionLength::ZERO); + + env->SetSymbolFunction(thread_, symbolFunction); + env->SetHasInstanceSymbol(thread_, hasInstanceSymbol); + env->SetIsConcatSpreadableSymbol(thread_, isConcatSpreadableSymbol); + env->SetToStringTagSymbol(thread_, toStringTagSymbol); + env->SetIteratorSymbol(thread_, iteratorSymbol); + env->SetMatchSymbol(thread_, matchSymbol); + env->SetReplaceSymbol(thread_, replaceSymbol); + env->SetSearchSymbol(thread_, searchSymbol); + env->SetSpeciesSymbol(thread_, speciesSymbol); + env->SetSplitSymbol(thread_, splitSymbol); + env->SetToPrimitiveSymbol(thread_, toPrimitiveSymbol); + env->SetUnscopablesSymbol(thread_, unscopablesSymbol); + + // Setup %SymbolPrototype% + SetStringTagSymbol(env, symbolFuncPrototype, "Symbol"); + + JSHandle holeySymbol(factory_->NewPrivateNameSymbolWithChar("holey")); + env->SetHoleySymbol(thread_, holeySymbol.GetTaggedValue()); + JSHandle elementIcSymbol(factory_->NewPrivateNameSymbolWithChar("element-ic")); + env->SetElementICSymbol(thread_, elementIcSymbol.GetTaggedValue()); + + // ecma 19.2.3.6 Function.prototype[@@hasInstance] ( V ) + JSHandle funcFuncPrototypeObj = JSHandle(env->GetFunctionPrototype()); + SetFunctionAtSymbol( + env, funcFuncPrototypeObj, env->GetHasInstanceSymbol(), "[Symbol.hasInstance]", + Function::FunctionPrototypeHasInstance, FunctionLength::ONE); +} + +void Builtins::InitializeSymbolWithRealm(const JSHandle &realm, + const JSHandle &objFuncInstanceDynclass) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle env = vm_->GetGlobalEnv(); + // Symbol.prototype + JSHandle symbolFuncPrototype = factory_->NewJSObject(objFuncInstanceDynclass); + JSHandle symbolFuncPrototypeValue(symbolFuncPrototype); + + // Symbol.prototype_or_dynclass + JSHandle symbolFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSPrimitiveRef::SIZE, JSType::JS_PRIMITIVE_REF, symbolFuncPrototypeValue); + + // Symbol = new Function() + JSHandle symbolFunction( + NewBuiltinConstructor(realm, symbolFuncPrototype, Symbol::SymbolConstructor, "Symbol", FunctionLength::ZERO)); + JSHandle(symbolFunction)->SetFunctionPrototype(thread_, symbolFuncInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = thread_->GlobalConstants()->GetHandledConstructorString(); + PropertyDescriptor descriptor(thread_, JSHandle::Cast(symbolFunction), true, false, true); + JSObject::DefineOwnProperty(thread_, symbolFuncPrototype, constructorKey, descriptor); + + SetFunction(realm, symbolFunction, "for", Symbol::For, FunctionLength::ONE); + SetFunction(realm, symbolFunction, "keyFor", Symbol::KeyFor, FunctionLength::ONE); + + // Symbol attribute + SetNoneAttributeProperty(symbolFunction, "hasInstance", env->GetHasInstanceSymbol()); + SetNoneAttributeProperty(symbolFunction, "isConcatSpreadable", env->GetIsConcatSpreadableSymbol()); + SetNoneAttributeProperty(symbolFunction, "toStringTag", env->GetToStringTagSymbol()); + SetNoneAttributeProperty(symbolFunction, "iterator", env->GetIteratorSymbol()); + SetNoneAttributeProperty(symbolFunction, "match", env->GetMatchSymbol()); + SetNoneAttributeProperty(symbolFunction, "replace", env->GetReplaceSymbol()); + SetNoneAttributeProperty(symbolFunction, "search", env->GetSearchSymbol()); + SetNoneAttributeProperty(symbolFunction, "species", env->GetSpeciesSymbol()); + SetNoneAttributeProperty(symbolFunction, "split", env->GetSplitSymbol()); + SetNoneAttributeProperty(symbolFunction, "toPrimitive", env->GetToPrimitiveSymbol()); + SetNoneAttributeProperty(symbolFunction, "unscopables", env->GetUnscopablesSymbol()); + + // symbol.prototype.description + PropertyDescriptor descriptionDesc(thread_); + JSHandle getterKey(factory_->NewFromString("description")); + JSHandle getter(factory_->NewJSFunction(realm, reinterpret_cast(Symbol::DescriptionGetter))); + SetGetter(symbolFuncPrototype, getterKey, getter); + + // Setup symbol.prototype[@@toPrimitive] + SetFunctionAtSymbol(realm, symbolFuncPrototype, env->GetToPrimitiveSymbol(), + "[Symbol.toPrimitive]", Symbol::ToPrimitive, + FunctionLength::ONE); + // install the Symbol.prototype methods + SetFunction(realm, symbolFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Symbol::ToString, + FunctionLength::ZERO); + SetFunction(realm, symbolFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), Symbol::ValueOf, + FunctionLength::ZERO); + + realm->SetSymbolFunction(thread_, symbolFunction); + realm->SetHasInstanceSymbol(thread_, env->GetHasInstanceSymbol()); + realm->SetIsConcatSpreadableSymbol(thread_, env->GetIsConcatSpreadableSymbol()); + realm->SetToStringTagSymbol(thread_, env->GetToStringTagSymbol()); + realm->SetIteratorSymbol(thread_, env->GetIteratorSymbol()); + realm->SetMatchSymbol(thread_, env->GetMatchSymbol()); + realm->SetReplaceSymbol(thread_, env->GetReplaceSymbol()); + realm->SetSearchSymbol(thread_, env->GetSearchSymbol()); + realm->SetSpeciesSymbol(thread_, env->GetSpeciesSymbol()); + realm->SetSplitSymbol(thread_, env->GetSplitSymbol()); + realm->SetToPrimitiveSymbol(thread_, env->GetToPrimitiveSymbol()); + realm->SetUnscopablesSymbol(thread_, env->GetUnscopablesSymbol()); + + // Setup %SymbolPrototype% + SetStringTagSymbol(realm, symbolFuncPrototype, "Symbol"); + + JSHandle holeySymbol(factory_->NewPrivateNameSymbolWithChar("holey")); + realm->SetHoleySymbol(thread_, holeySymbol.GetTaggedValue()); + JSHandle elementIcSymbol(factory_->NewPrivateNameSymbolWithChar("element-ic")); + realm->SetElementICSymbol(thread_, elementIcSymbol.GetTaggedValue()); + + // ecma 19.2.3.6 Function.prototype[@@hasInstance] ( V ) + JSHandle funcFuncPrototypeObj = JSHandle(realm->GetFunctionPrototype()); + SetFunctionAtSymbol( + realm, funcFuncPrototypeObj, realm->GetHasInstanceSymbol(), "[Symbol.hasInstance]", + Function::FunctionPrototypeHasInstance, FunctionLength::ONE); +} + +void Builtins::InitializeNumber(const JSHandle &env, const JSHandle &globalObject, + const JSHandle &primRefObjDynclass) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Number.prototype + JSHandle toObject(thread_, JSTaggedValue(FunctionLength::ZERO)); + JSHandle numFuncPrototype = + JSHandle::Cast(factory_->NewJSPrimitiveRef(primRefObjDynclass, toObject)); + JSHandle numFuncPrototypeValue(numFuncPrototype); + + // Number.prototype_or_dynclass + JSHandle numFuncInstanceClass = + factory_->NewEcmaDynClass(JSPrimitiveRef::SIZE, JSType::JS_PRIMITIVE_REF, numFuncPrototypeValue); + + // Number = new Function() + JSHandle numFunction( + NewBuiltinConstructor(env, numFuncPrototype, Number::NumberConstructor, "Number", FunctionLength::ONE)); + numFunction.GetObject()->SetFunctionPrototype(thread_, numFuncInstanceClass.GetTaggedValue()); + + // Number.prototype method + SetFunction(env, numFuncPrototype, "toExponential", Number::ToExponential, FunctionLength::ONE); + SetFunction(env, numFuncPrototype, "toFixed", Number::ToFixed, FunctionLength::ONE); + SetFunction(env, numFuncPrototype, "toPrecision", Number::ToPrecision, FunctionLength::ONE); + SetFunction(env, numFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Number::ToString, + FunctionLength::ONE); + SetFunction(env, numFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), Number::ValueOf, + FunctionLength::ZERO); + + // Number method + SetFunction(env, numFunction, "isFinite", Number::IsFinite, FunctionLength::ONE); + SetFunction(env, numFunction, "isInteger", Number::IsInteger, FunctionLength::ONE); + SetFunction(env, numFunction, "isNaN", Number::IsNaN, FunctionLength::ONE); + SetFunction(env, numFunction, "isSafeInteger", Number::IsSafeInteger, FunctionLength::ONE); + SetFuncToObjAndGlobal(env, globalObject, numFunction, "parseFloat", Number::ParseFloat, FunctionLength::ONE); + SetFuncToObjAndGlobal(env, globalObject, numFunction, "parseInt", Number::ParseInt, FunctionLength::TWO); + + // Number constant + const double epsilon = 2.220446049250313e-16; + const double maxSafeInteger = 9007199254740991; + const double maxValue = 1.7976931348623157e+308; + const double minValue = 5e-324; + const double positiveInfinity = std::numeric_limits::infinity(); + SetConstant(numFunction, "MAX_VALUE", JSTaggedValue(maxValue)); + SetConstant(numFunction, "MIN_VALUE", JSTaggedValue(minValue)); + SetConstant(numFunction, "NaN", JSTaggedValue(NAN)); + SetConstant(numFunction, "NEGATIVE_INFINITY", JSTaggedValue(-positiveInfinity)); + SetConstant(numFunction, "POSITIVE_INFINITY", JSTaggedValue(positiveInfinity)); + SetConstant(numFunction, "MAX_SAFE_INTEGER", JSTaggedValue(maxSafeInteger)); + SetConstant(numFunction, "MIN_SAFE_INTEGER", JSTaggedValue(-maxSafeInteger)); + SetConstant(numFunction, "EPSILON", JSTaggedValue(epsilon)); + + env->SetNumberFunction(thread_, numFunction); +} + +void Builtins::InitializeDate(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const int utcLength = 7; + // Date.prototype + JSHandle dateFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle dateFuncPrototypeValue(dateFuncPrototype); + + // Date.prototype_or_dynclass + JSHandle dateFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSDate::SIZE, JSType::JS_DATE, dateFuncPrototypeValue); + + // Date = new Function() + JSHandle dateFunction( + NewBuiltinConstructor(env, dateFuncPrototype, Date::DateConstructor, "Date", FunctionLength::ONE)); + JSHandle(dateFunction)->SetFunctionPrototype(thread_, dateFuncInstanceDynclass.GetTaggedValue()); + + // Date.prototype method + SetFunction(env, dateFuncPrototype, "getDate", Date::GetDate, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getDay", Date::GetDay, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getFullYear", Date::GetFullYear, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getHours", Date::GetHours, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getMilliseconds", Date::GetMilliseconds, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getMinutes", Date::GetMinutes, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getMonth", Date::GetMonth, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getSeconds", Date::GetSeconds, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getTime", Date::GetTime, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getTimezoneOffset", Date::GetTimezoneOffset, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCDate", Date::GetUTCDate, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCDay", Date::GetUTCDay, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCFullYear", Date::GetUTCFullYear, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCHours", Date::GetUTCHours, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCMilliseconds", Date::GetUTCMilliseconds, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCMinutes", Date::GetUTCMinutes, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCMonth", Date::GetUTCMonth, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCSeconds", Date::GetUTCSeconds, FunctionLength::ZERO); + + SetFunction(env, dateFuncPrototype, "setDate", Date::SetDate, FunctionLength::ONE); + SetFunction(env, dateFuncPrototype, "setFullYear", Date::SetFullYear, FunctionLength::THREE); + SetFunction(env, dateFuncPrototype, "setHours", Date::SetHours, FunctionLength::FOUR); + SetFunction(env, dateFuncPrototype, "setMilliseconds", Date::SetMilliseconds, FunctionLength::ONE); + SetFunction(env, dateFuncPrototype, "setMinutes", Date::SetMinutes, FunctionLength::THREE); + SetFunction(env, dateFuncPrototype, "setMonth", Date::SetMonth, FunctionLength::TWO); + SetFunction(env, dateFuncPrototype, "setSeconds", Date::SetSeconds, FunctionLength::TWO); + SetFunction(env, dateFuncPrototype, "setTime", Date::SetTime, FunctionLength::ONE); + SetFunction(env, dateFuncPrototype, "setUTCDate", Date::SetUTCDate, FunctionLength::ONE); + SetFunction(env, dateFuncPrototype, "setUTCFullYear", Date::SetUTCFullYear, FunctionLength::THREE); + SetFunction(env, dateFuncPrototype, "setUTCHours", Date::SetUTCHours, FunctionLength::FOUR); + SetFunction(env, dateFuncPrototype, "setUTCMilliseconds", Date::SetUTCMilliseconds, FunctionLength::ONE); + SetFunction(env, dateFuncPrototype, "setUTCMinutes", Date::SetUTCMinutes, FunctionLength::THREE); + SetFunction(env, dateFuncPrototype, "setUTCMonth", Date::SetUTCMonth, FunctionLength::TWO); + SetFunction(env, dateFuncPrototype, "setUTCSeconds", Date::SetUTCSeconds, FunctionLength::TWO); + + SetFunction(env, dateFuncPrototype, "toDateString", Date::ToDateString, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "toISOString", Date::ToISOString, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "toJSON", Date::ToJSON, FunctionLength::ONE); + SetFunction(env, dateFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Date::ToString, + FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "toTimeString", Date::ToTimeString, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "toUTCString", Date::ToUTCString, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), Date::ValueOf, + FunctionLength::ZERO); + + SetFunctionAtSymbol(env, dateFuncPrototype, env->GetToPrimitiveSymbol(), "[Symbol.toPrimitive]", Date::ToPrimitive, + FunctionLength::ONE); + + // Date method + SetFunction(env, dateFunction, "now", Date::Now, FunctionLength::ZERO); + SetFunction(env, dateFunction, "parse", Date::Parse, FunctionLength::ONE); + SetFunction(env, dateFunction, "UTC", Date::UTC, utcLength); + + // Date.length + SetConstant(dateFunction, "length", JSTaggedValue(utcLength)); + + env->SetDateFunction(thread_, dateFunction); +} + +void Builtins::InitializeBoolean(const JSHandle &env, const JSHandle &primRefObjDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Boolean.prototype + JSHandle toObject(thread_, JSTaggedValue::False()); + JSHandle booleanFuncPrototype = + JSHandle::Cast(factory_->NewJSPrimitiveRef(primRefObjDynclass, toObject)); + JSHandle booleanFuncPrototypeValue(booleanFuncPrototype); + + // Boolean.prototype_or_dynclass + JSHandle booleanFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSPrimitiveRef::SIZE, JSType::JS_PRIMITIVE_REF, booleanFuncPrototypeValue); + + // new Boolean Function() + JSHandle booleanFunction( + NewBuiltinConstructor(env, booleanFuncPrototype, Boolean::BooleanConstructor, "Boolean", FunctionLength::ONE)); + booleanFunction->SetFunctionPrototype(thread_, booleanFuncInstanceDynclass.GetTaggedValue()); + + // Boolean.prototype method + SetFunction(env, booleanFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), + Boolean::BooleanPrototypeToString, FunctionLength::ZERO); + SetFunction(env, booleanFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), + Boolean::BooleanPrototypeValueOf, FunctionLength::ZERO); + + env->SetBooleanFunction(thread_, booleanFunction); +} + +void Builtins::InitializeProxy(const JSHandle &env) +{ + JSHandle proxyFunction(InitializeExoticConstructor(env, Proxy::ProxyConstructor, "Proxy", 2)); + + // Proxy method + SetFunction(env, proxyFunction, "revocable", Proxy::Revocable, FunctionLength::TWO); + env->SetProxyFunction(thread_, proxyFunction); +} + +JSHandle Builtins::InitializeExoticConstructor(const JSHandle &env, EcmaEntrypoint ctorFunc, + const char *name, int length) +{ + JSHandle ctor = + factory_->NewJSFunction(env, reinterpret_cast(ctorFunc), FunctionKind::BUILTIN_PROXY_CONSTRUCTOR); + + JSFunction::SetFunctionLength(thread_, ctor, JSTaggedValue(length)); + JSHandle nameString(factory_->NewFromString(name)); + JSFunction::SetFunctionName(thread_, JSHandle(ctor), nameString, + JSHandle(thread_, JSTaggedValue::Undefined())); + + JSHandle globalObject(thread_, env->GetGlobalObject()); + PropertyDescriptor descriptor(thread_, JSHandle(ctor), true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, nameString, descriptor); + return ctor; +} + +void Builtins::InitializeAsyncFunction(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // AsyncFunction.prototype + JSHandle asyncFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSObject::SetPrototype(thread_, asyncFuncPrototype, env->GetFunctionPrototype()); + JSHandle async_func_prototype_value(asyncFuncPrototype); + + // AsyncFunction.prototype_or_dynclass + JSHandle asyncFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSAsyncFunction::SIZE, JSType::JS_ASYNC_FUNCTION, async_func_prototype_value); + + // AsyncFunction = new Function() + JSHandle asyncFunction = NewBuiltinConstructor( + env, asyncFuncPrototype, AsyncFunction::AsyncFunctionConstructor, "AsyncFunction", FunctionLength::ONE); + JSObject::SetPrototype(thread_, JSHandle::Cast(asyncFunction), env->GetFunctionFunction()); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor asyncDesc(thread_, JSHandle::Cast(asyncFunction), false, false, true); + JSObject::DefineOwnProperty(thread_, asyncFuncPrototype, constructorKey, asyncDesc); + asyncFunction->SetProtoOrDynClass(thread_, asyncFuncInstanceDynclass.GetTaggedValue()); + + // AsyncFunction.prototype property + SetStringTagSymbol(env, asyncFuncPrototype, "AsyncFunction"); + env->SetAsyncFunction(thread_, asyncFunction); + env->SetAsyncFunctionPrototype(thread_, asyncFuncPrototype); +} + +void Builtins::InitializeAllTypeError(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + // Error.prototype + JSHandle errorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle errorFuncPrototypeValue(errorFuncPrototype); + // Error.prototype_or_dynclass + JSHandle errorFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_ERROR, errorFuncPrototypeValue); + // Error() = new Function() + JSHandle errorFunction( + NewBuiltinConstructor(env, errorFuncPrototype, Error::ErrorConstructor, "Error", FunctionLength::ONE)); + errorFunction->SetFunctionPrototype(thread_, errorFuncInstanceDynclass.GetTaggedValue()); + + // Error.prototype method + SetFunction(env, errorFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Error::ToString, + FunctionLength::ZERO); + + // Error.prototype Attribute + SetAttribute(errorFuncPrototype, "name", "Error"); + SetAttribute(errorFuncPrototype, "message", ""); + env->SetErrorFunction(thread_, errorFunction); + + JSHandle nativeErrorFuncClass = + factory_->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, env->GetErrorFunction()); + nativeErrorFuncClass->SetConstructor(true); + JSHandle function = JSHandle::Cast(factory_->NewJSObject(nativeErrorFuncClass)); + function->SetBuiltinsCtorMode(); + env->SetNativeErrorFunctionClass(thread_, nativeErrorFuncClass); + + JSHandle errorNativeFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, errorFuncPrototypeValue); + InitializeError(env, errorNativeFuncInstanceDynclass, JSType::JS_RANGE_ERROR); + InitializeError(env, errorNativeFuncInstanceDynclass, JSType::JS_REFERENCE_ERROR); + InitializeError(env, errorNativeFuncInstanceDynclass, JSType::JS_TYPE_ERROR); + InitializeError(env, errorNativeFuncInstanceDynclass, JSType::JS_URI_ERROR); + InitializeError(env, errorNativeFuncInstanceDynclass, JSType::JS_SYNTAX_ERROR); + InitializeError(env, errorNativeFuncInstanceDynclass, JSType::JS_EVAL_ERROR); +} + +void Builtins::InitializeAllTypeErrorWithRealm(const JSHandle &realm) const +{ + JSHandle env = vm_->GetGlobalEnv(); + + realm->SetErrorFunction(thread_, env->GetErrorFunction()); + realm->SetNativeErrorFunctionClass(thread_, env->GetNativeErrorFunctionClass()); + + SetErrorWithRealm(realm, JSType::JS_RANGE_ERROR); + SetErrorWithRealm(realm, JSType::JS_REFERENCE_ERROR); + SetErrorWithRealm(realm, JSType::JS_TYPE_ERROR); + SetErrorWithRealm(realm, JSType::JS_URI_ERROR); + SetErrorWithRealm(realm, JSType::JS_SYNTAX_ERROR); + SetErrorWithRealm(realm, JSType::JS_EVAL_ERROR); +} + +void Builtins::SetErrorWithRealm(const JSHandle &realm, const JSType &errorTag) const +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle globalObject(thread_, realm->GetGlobalObject()); + JSHandle nameString; + JSHandle nativeErrorFunction; + switch (errorTag) { + case JSType::JS_RANGE_ERROR: + nativeErrorFunction = env->GetRangeErrorFunction(); + nameString = JSHandle(thread_->GlobalConstants()->GetHandledRangeErrorString()); + realm->SetRangeErrorFunction(thread_, nativeErrorFunction); + break; + case JSType::JS_EVAL_ERROR: + nativeErrorFunction = env->GetEvalErrorFunction(); + nameString = JSHandle(thread_->GlobalConstants()->GetHandledEvalErrorString()); + realm->SetEvalErrorFunction(thread_, nativeErrorFunction); + break; + case JSType::JS_REFERENCE_ERROR: + nativeErrorFunction = env->GetReferenceErrorFunction(); + nameString = JSHandle(thread_->GlobalConstants()->GetHandledReferenceErrorString()); + realm->SetReferenceErrorFunction(thread_, nativeErrorFunction); + break; + case JSType::JS_TYPE_ERROR: + nativeErrorFunction = env->GetTypeErrorFunction(); + nameString = JSHandle(thread_->GlobalConstants()->GetHandledTypeErrorString()); + realm->SetTypeErrorFunction(thread_, nativeErrorFunction); + realm->SetThrowTypeError(thread_, env->GetThrowTypeError()); + break; + case JSType::JS_URI_ERROR: + nativeErrorFunction = env->GetURIErrorFunction(); + nameString = JSHandle(thread_->GlobalConstants()->GetHandledURIErrorString()); + realm->SetURIErrorFunction(thread_, nativeErrorFunction); + break; + case JSType::JS_SYNTAX_ERROR: + nativeErrorFunction = env->GetSyntaxErrorFunction(); + nameString = JSHandle(thread_->GlobalConstants()->GetHandledSyntaxErrorString()); + realm->SetSyntaxErrorFunction(thread_, nativeErrorFunction); + break; + default: + break; + } + PropertyDescriptor descriptor(thread_, nativeErrorFunction, true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, nameString, descriptor); +} + +void Builtins::GeneralUpdateError(ErrorParameter *error, EcmaEntrypoint constructor, EcmaEntrypoint method, + const char *name, JSType type) const +{ + error->nativeConstructor = constructor; + error->nativeMethod = method; + error->nativePropertyName = name; + error->nativeJstype = type; +} + +void Builtins::InitializeError(const JSHandle &env, const JSHandle &objFuncDynclass, + const JSType &errorTag) const +{ + // NativeError.prototype + JSHandle nativeErrorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle nativeErrorFuncPrototypeValue(nativeErrorFuncPrototype); + + ErrorParameter errorParameter{RangeError::RangeErrorConstructor, RangeError::ToString, "RangeError", + JSType::JS_RANGE_ERROR}; + switch (errorTag) { + case JSType::JS_RANGE_ERROR: + GeneralUpdateError(&errorParameter, RangeError::RangeErrorConstructor, RangeError::ToString, "RangeError", + JSType::JS_RANGE_ERROR); + break; + case JSType::JS_EVAL_ERROR: + GeneralUpdateError(&errorParameter, EvalError::EvalErrorConstructor, EvalError::ToString, "EvalError", + JSType::JS_EVAL_ERROR); + break; + case JSType::JS_REFERENCE_ERROR: + GeneralUpdateError(&errorParameter, ReferenceError::ReferenceErrorConstructor, ReferenceError::ToString, + "ReferenceError", JSType::JS_REFERENCE_ERROR); + break; + case JSType::JS_TYPE_ERROR: + GeneralUpdateError(&errorParameter, TypeError::TypeErrorConstructor, TypeError::ToString, "TypeError", + JSType::JS_TYPE_ERROR); + break; + case JSType::JS_URI_ERROR: + GeneralUpdateError(&errorParameter, URIError::URIErrorConstructor, URIError::ToString, "URIError", + JSType::JS_URI_ERROR); + break; + case JSType::JS_SYNTAX_ERROR: + GeneralUpdateError(&errorParameter, SyntaxError::SyntaxErrorConstructor, SyntaxError::ToString, + "SyntaxError", JSType::JS_SYNTAX_ERROR); + break; + default: + break; + } + + // NativeError.prototype_or_dynclass + JSHandle nativeErrorFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, errorParameter.nativeJstype, nativeErrorFuncPrototypeValue); + + // NativeError() = new Error() + JSHandle nativeErrorFunction = + factory_->NewJSNativeErrorFunction(env, reinterpret_cast(errorParameter.nativeConstructor)); + InitializeCtor(env, nativeErrorFuncPrototype, nativeErrorFunction, errorParameter.nativePropertyName, + FunctionLength::ONE); + + nativeErrorFunction->SetFunctionPrototype(thread_, nativeErrorFuncInstanceDynclass.GetTaggedValue()); + + // NativeError.prototype method + SetFunction(env, nativeErrorFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), + errorParameter.nativeMethod, FunctionLength::ZERO); + + // Error.prototype Attribute + SetAttribute(nativeErrorFuncPrototype, "name", errorParameter.nativePropertyName); + SetAttribute(nativeErrorFuncPrototype, "message", ""); + + if (errorTag == JSType::JS_RANGE_ERROR) { + env->SetRangeErrorFunction(thread_, nativeErrorFunction); + } else if (errorTag == JSType::JS_REFERENCE_ERROR) { + env->SetReferenceErrorFunction(thread_, nativeErrorFunction); + } else if (errorTag == JSType::JS_TYPE_ERROR) { + env->SetTypeErrorFunction(thread_, nativeErrorFunction); + JSHandle throwTypeErrorFunction = + factory_->NewJSFunction(env, reinterpret_cast(TypeError::ThrowTypeError)); + JSFunction::SetFunctionLength(thread_, throwTypeErrorFunction, JSTaggedValue(1), false); + JSObject::PreventExtensions(thread_, JSHandle::Cast(throwTypeErrorFunction)); + env->SetThrowTypeError(thread_, throwTypeErrorFunction); + } else if (errorTag == JSType::JS_URI_ERROR) { + env->SetURIErrorFunction(thread_, nativeErrorFunction); + } else if (errorTag == JSType::JS_SYNTAX_ERROR) { + env->SetSyntaxErrorFunction(thread_, nativeErrorFunction); + } else { + env->SetEvalErrorFunction(thread_, nativeErrorFunction); + } +} // namespace panda::ecmascript + +void Builtins::InitializeCtor(const JSHandle &env, const JSHandle &prototype, + const JSHandle &ctor, const char *name, int length) const +{ + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSFunction::SetFunctionLength(thread_, ctor, JSTaggedValue(length)); + JSHandle nameString(factory_->NewFromString(name)); + JSFunction::SetFunctionName(thread_, JSHandle(ctor), nameString, + JSHandle(thread_, JSTaggedValue::Undefined())); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor descriptor1(thread_, JSHandle::Cast(ctor), true, false, true); + JSObject::DefineOwnProperty(thread_, prototype, constructorKey, descriptor1); + + /* set "prototype" in constructor */ + ctor->SetFunctionPrototype(thread_, prototype.GetTaggedValue()); + + if (!JSTaggedValue::SameValue(nameString, thread_->GlobalConstants()->GetHandledAsyncFunctionString())) { + JSHandle globalObject(thread_, env->GetGlobalObject()); + PropertyDescriptor descriptor2(thread_, JSHandle::Cast(ctor), true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, nameString, descriptor2); + } +} + +void Builtins::InitializeSet(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // Set.prototype + JSHandle setFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle setFuncPrototypeValue(setFuncPrototype); + // Set.prototype_or_dynclass + JSHandle setFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSSet::SIZE, JSType::JS_SET, setFuncPrototypeValue); + // Set() = new Function() + JSHandle setFunction( + NewBuiltinConstructor(env, setFuncPrototype, BuiltinsSet::SetConstructor, "Set", FunctionLength::ZERO)); + JSHandle(setFunction)->SetFunctionPrototype(thread_, setFuncInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread_, JSHandle(setFuncPrototype), constructorKey, setFunction); + // set.prototype.add() + SetFunction(env, setFuncPrototype, "add", BuiltinsSet::Add, FunctionLength::ONE); + // set.prototype.clear() + SetFunction(env, setFuncPrototype, "clear", BuiltinsSet::Clear, FunctionLength::ZERO); + // set.prototype.delete() + SetFunction(env, setFuncPrototype, "delete", BuiltinsSet::Delete, FunctionLength::ONE); + // set.prototype.has() + SetFunction(env, setFuncPrototype, "has", BuiltinsSet::Has, FunctionLength::ONE); + // set.prototype.forEach() + SetFunction(env, setFuncPrototype, "forEach", BuiltinsSet::ForEach, FunctionLength::ONE); + // set.prototype.entries() + SetFunction(env, setFuncPrototype, "entries", BuiltinsSet::Entries, FunctionLength::ZERO); + // set.prototype.keys() + SetFunction(env, setFuncPrototype, "values", BuiltinsSet::Values, FunctionLength::ZERO); + // set.prototype.values() + JSHandle keys(factory_->NewFromString("keys")); + JSHandle values(factory_->NewFromString("values")); + JSHandle valuesFunc = + JSObject::GetMethod(thread_, JSHandle::Cast(setFuncPrototype), values); + PropertyDescriptor descriptor(thread_, valuesFunc, true, false, true); + JSObject::DefineOwnProperty(thread_, setFuncPrototype, keys, descriptor); + + // @@ToStringTag + SetStringTagSymbol(env, setFuncPrototype, "Set"); + + // 23.1.3.10get Set.prototype.size + JSHandle sizeGetter = CreateGetter(env, BuiltinsSet::GetSize, "size", FunctionLength::ZERO); + JSHandle sizeKey(factory_->NewFromString("size")); + SetGetter(setFuncPrototype, sizeKey, sizeGetter); + + // 23.1.2.2get Set [ @@species ] + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesGetter = + CreateGetter(env, BuiltinsSet::Species, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(JSHandle(setFunction), speciesSymbol, speciesGetter); + + // %SetPrototype% [ @@iterator ] + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSObject::DefineOwnProperty(thread_, setFuncPrototype, iteratorSymbol, descriptor); + + env->SetBuiltinsSetFunction(thread_, setFunction); +} + +void Builtins::InitializeMap(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // Map.prototype + JSHandle mapFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle mapFuncPrototypeValue(mapFuncPrototype); + // Map.prototype_or_dynclass + JSHandle mapFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSMap::SIZE, JSType::JS_MAP, mapFuncPrototypeValue); + // Map() = new Function() + JSHandle mapFunction( + NewBuiltinConstructor(env, mapFuncPrototype, BuiltinsMap::MapConstructor, "Map", FunctionLength::ZERO)); + // Map().prototype = Map.Prototype & Map.prototype.constructor = Map() + JSFunction::Cast(mapFunction->GetTaggedObject()) + ->SetFunctionPrototype(thread_, mapFuncInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread_, JSHandle(mapFuncPrototype), constructorKey, mapFunction); + // map.prototype.set() + SetFunction(env, mapFuncPrototype, globalConst->GetHandledSetString(), BuiltinsMap::Set, FunctionLength::TWO); + // map.prototype.clear() + SetFunction(env, mapFuncPrototype, "clear", BuiltinsMap::Clear, FunctionLength::ZERO); + // map.prototype.delete() + SetFunction(env, mapFuncPrototype, "delete", BuiltinsMap::Delete, FunctionLength::ONE); + // map.prototype.has() + SetFunction(env, mapFuncPrototype, "has", BuiltinsMap::Has, FunctionLength::ONE); + // map.prototype.get() + SetFunction(env, mapFuncPrototype, thread_->GlobalConstants()->GetHandledGetString(), BuiltinsMap::Get, + FunctionLength::ONE); + // map.prototype.forEach() + SetFunction(env, mapFuncPrototype, "forEach", BuiltinsMap::ForEach, FunctionLength::ONE); + // map.prototype.keys() + SetFunction(env, mapFuncPrototype, "keys", BuiltinsMap::Keys, FunctionLength::ZERO); + // map.prototype.values() + SetFunction(env, mapFuncPrototype, "values", BuiltinsMap::Values, FunctionLength::ZERO); + // map.prototype.entries() + SetFunction(env, mapFuncPrototype, "entries", BuiltinsMap::Entries, FunctionLength::ZERO); + // @@ToStringTag + SetStringTagSymbol(env, mapFuncPrototype, "Map"); + + // 23.1.3.10get Map.prototype.size + JSHandle sizeGetter = CreateGetter(env, BuiltinsMap::GetSize, "size", FunctionLength::ZERO); + JSHandle sizeKey(factory_->NewFromString("size")); + SetGetter(mapFuncPrototype, sizeKey, sizeGetter); + + // 23.1.2.2get Map [ @@species ] + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesGetter = + CreateGetter(env, BuiltinsMap::Species, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(JSHandle(mapFunction), speciesSymbol, speciesGetter); + + // %MapPrototype% [ @@iterator ] + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle entries(factory_->NewFromString("entries")); + JSHandle entriesFunc = + JSObject::GetMethod(thread_, JSHandle::Cast(mapFuncPrototype), entries); + PropertyDescriptor descriptor(thread_, entriesFunc, true, false, true); + JSObject::DefineOwnProperty(thread_, mapFuncPrototype, iteratorSymbol, descriptor); + + env->SetBuiltinsMapFunction(thread_, mapFunction); + env->SetMapPrototype(thread_, mapFuncPrototype); +} + +void Builtins::InitializeWeakMap(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // WeakMap.prototype + JSHandle weakMapFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle weakMapFuncPrototypeValue(weakMapFuncPrototype); + // WeakMap.prototype_or_dynclass + JSHandle weakMapFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSWeakMap::SIZE, JSType::JS_WEAK_MAP, weakMapFuncPrototypeValue); + // WeakMap() = new Function() + JSHandle weakMapFunction(NewBuiltinConstructor( + env, weakMapFuncPrototype, BuiltinsWeakMap::WeakMapConstructor, "WeakMap", FunctionLength::ZERO)); + // WeakMap().prototype = WeakMap.Prototype & WeakMap.prototype.constructor = WeakMap() + JSFunction::Cast(weakMapFunction->GetTaggedObject()) + ->SetProtoOrDynClass(thread_, weakMapFuncInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread_, JSHandle(weakMapFuncPrototype), constructorKey, weakMapFunction); + // weakmap.prototype.set() + SetFunction(env, weakMapFuncPrototype, globalConst->GetHandledSetString(), BuiltinsWeakMap::Set, + FunctionLength::TWO); + // weakmap.prototype.delete() + SetFunction(env, weakMapFuncPrototype, "delete", BuiltinsWeakMap::Delete, FunctionLength::ONE); + // weakmap.prototype.has() + SetFunction(env, weakMapFuncPrototype, "has", BuiltinsWeakMap::Has, FunctionLength::ONE); + // weakmap.prototype.get() + SetFunction(env, weakMapFuncPrototype, thread_->GlobalConstants()->GetHandledGetString(), BuiltinsWeakMap::Get, + FunctionLength::ONE); + // @@ToStringTag + SetStringTagSymbol(env, weakMapFuncPrototype, "WeakMap"); + + env->SetBuiltinsWeakMapFunction(thread_, weakMapFunction); +} + +void Builtins::InitializeWeakSet(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // Set.prototype + JSHandle weakSetFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle weakSetFuncPrototypeValue(weakSetFuncPrototype); + // Set.prototype_or_dynclass + JSHandle weakSetFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSWeakSet::SIZE, JSType::JS_WEAK_SET, weakSetFuncPrototypeValue); + // Set() = new Function() + JSHandle weakSetFunction(NewBuiltinConstructor( + env, weakSetFuncPrototype, BuiltinsWeakSet::WeakSetConstructor, "WeakSet", FunctionLength::ZERO)); + JSHandle(weakSetFunction)->SetProtoOrDynClass(thread_, weakSetFuncInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread_, JSHandle(weakSetFuncPrototype), constructorKey, weakSetFunction); + // set.prototype.add() + SetFunction(env, weakSetFuncPrototype, "add", BuiltinsWeakSet::Add, FunctionLength::ONE); + // set.prototype.delete() + SetFunction(env, weakSetFuncPrototype, "delete", BuiltinsWeakSet::Delete, FunctionLength::ONE); + // set.prototype.has() + SetFunction(env, weakSetFuncPrototype, "has", BuiltinsWeakSet::Has, FunctionLength::ONE); + + // @@ToStringTag + SetStringTagSymbol(env, weakSetFuncPrototype, "WeakSet"); + + env->SetBuiltinsWeakSetFunction(thread_, weakSetFunction); +} + +void Builtins::InitializeMath(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle mathDynclass = factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, objFuncPrototypeVal); + JSHandle mathObject = factory_->NewJSObject(mathDynclass); + SetFunction(env, mathObject, "abs", Math::Abs, FunctionLength::ONE); + SetFunction(env, mathObject, "acos", Math::Acos, FunctionLength::ONE); + SetFunction(env, mathObject, "acosh", Math::Acosh, FunctionLength::ONE); + SetFunction(env, mathObject, "asin", Math::Asin, FunctionLength::ONE); + SetFunction(env, mathObject, "asinh", Math::Asinh, FunctionLength::ONE); + SetFunction(env, mathObject, "atan", Math::Atan, FunctionLength::ONE); + SetFunction(env, mathObject, "atanh", Math::Atanh, FunctionLength::ONE); + SetFunction(env, mathObject, "atan2", Math::Atan2, FunctionLength::TWO); + SetFunction(env, mathObject, "cbrt", Math::Cbrt, FunctionLength::ONE); + SetFunction(env, mathObject, "ceil", Math::Ceil, FunctionLength::ONE); + SetFunction(env, mathObject, "clz32", Math::Clz32, FunctionLength::ONE); + SetFunction(env, mathObject, "cos", Math::Cos, FunctionLength::ONE); + SetFunction(env, mathObject, "cosh", Math::Cosh, FunctionLength::ONE); + SetFunction(env, mathObject, "exp", Math::Exp, FunctionLength::ONE); + SetFunction(env, mathObject, "expm1", Math::Expm1, FunctionLength::ONE); + SetFunction(env, mathObject, "floor", Math::Floor, FunctionLength::ONE); + SetFunction(env, mathObject, "fround", Math::Fround, FunctionLength::ONE); + SetFunction(env, mathObject, "hypot", Math::Hypot, FunctionLength::TWO); + SetFunction(env, mathObject, "imul", Math::Imul, FunctionLength::TWO); + SetFunction(env, mathObject, "log", Math::Log, FunctionLength::ONE); + SetFunction(env, mathObject, "log1p", Math::Log1p, FunctionLength::ONE); + SetFunction(env, mathObject, "log10", Math::Log10, FunctionLength::ONE); + SetFunction(env, mathObject, "log2", Math::Log2, FunctionLength::ONE); + SetFunction(env, mathObject, "max", Math::Max, FunctionLength::TWO); + SetFunction(env, mathObject, "min", Math::Min, FunctionLength::TWO); + SetFunction(env, mathObject, "pow", Math::Pow, FunctionLength::TWO); + SetFunction(env, mathObject, "random", Math::Random, FunctionLength::ZERO); + SetFunction(env, mathObject, "round", Math::Round, FunctionLength::ONE); + SetFunction(env, mathObject, "sign", Math::Sign, FunctionLength::ONE); + SetFunction(env, mathObject, "sin", Math::Sin, FunctionLength::ONE); + SetFunction(env, mathObject, "sinh", Math::Sinh, FunctionLength::ONE); + SetFunction(env, mathObject, "sqrt", Math::Sqrt, FunctionLength::ONE); + SetFunction(env, mathObject, "tan", Math::Tan, FunctionLength::ONE); + SetFunction(env, mathObject, "tanh", Math::Tanh, FunctionLength::ONE); + SetFunction(env, mathObject, "trunc", Math::Trunc, FunctionLength::ONE); + + SetConstant(mathObject, "E", JSTaggedValue(Math::E)); + SetConstant(mathObject, "LN10", JSTaggedValue(Math::LN10)); + SetConstant(mathObject, "LN2", JSTaggedValue(Math::LN2)); + SetConstant(mathObject, "LOG10E", JSTaggedValue(Math::LOG10E)); + SetConstant(mathObject, "LOG2E", JSTaggedValue(Math::LOG2E)); + SetConstant(mathObject, "PI", JSTaggedValue(Math::PI)); + SetConstant(mathObject, "SQRT1_2", JSTaggedValue(Math::SQRT1_2)); + SetConstant(mathObject, "SQRT2", JSTaggedValue(Math::SQRT2)); + + JSHandle mathString(factory_->NewFromString("Math")); + JSHandle globalObject(thread_, env->GetGlobalObject()); + PropertyDescriptor mathDesc(thread_, JSHandle::Cast(mathObject), true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, mathString, mathDesc); + // @@ToStringTag + SetStringTagSymbol(env, mathObject, "Math"); + env->SetMathFunction(thread_, mathObject); +} + +void Builtins::InitializeJson(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle jsonDynclass = factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, objFuncPrototypeVal); + JSHandle jsonObject = factory_->NewJSObject(jsonDynclass); + + SetFunction(env, jsonObject, "parse", Json::Parse, FunctionLength::TWO); + SetFunction(env, jsonObject, "stringify", Json::Stringify, FunctionLength::THREE); + + PropertyDescriptor jsonDesc(thread_, JSHandle::Cast(jsonObject), true, false, true); + JSHandle jsonString(factory_->NewFromString("JSON")); + JSHandle globalObject(thread_, env->GetGlobalObject()); + JSObject::DefineOwnProperty(thread_, globalObject, jsonString, jsonDesc); + // @@ToStringTag + SetStringTagSymbol(env, jsonObject, "JSON"); + env->SetJsonFunction(thread_, jsonObject); +} + +void Builtins::InitializeString(const JSHandle &env, const JSHandle &primRefObjDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // String.prototype + JSHandle toObject(factory_->GetEmptyString()); + JSHandle stringFuncPrototype = + JSHandle::Cast(factory_->NewJSPrimitiveRef(primRefObjDynclass, toObject)); + JSHandle stringFuncPrototypeValue(stringFuncPrototype); + + // String.prototype_or_dynclass + JSHandle stringFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSPrimitiveRef::SIZE, JSType::JS_PRIMITIVE_REF, stringFuncPrototypeValue); + + // String = new Function() + JSHandle stringFunction(NewBuiltinConstructor(env, stringFuncPrototype, BuiltinsString::StringConstructor, + "String", FunctionLength::ONE)); + stringFunction.GetObject()->SetFunctionPrototype(thread_, stringFuncInstanceDynclass.GetTaggedValue()); + + // String.prototype method + SetFunction(env, stringFuncPrototype, "charAt", BuiltinsString::CharAt, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "charCodeAt", BuiltinsString::CharCodeAt, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "codePointAt", BuiltinsString::CodePointAt, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "concat", BuiltinsString::Concat, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "endsWith", BuiltinsString::EndsWith, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "includes", BuiltinsString::Includes, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "indexOf", BuiltinsString::IndexOf, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "lastIndexOf", BuiltinsString::LastIndexOf, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "localeCompare", BuiltinsString::LocaleCompare, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "match", BuiltinsString::Match, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "repeat", BuiltinsString::Repeat, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "replace", BuiltinsString::Replace, FunctionLength::TWO); + SetFunction(env, stringFuncPrototype, "search", BuiltinsString::Search, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "slice", BuiltinsString::Slice, FunctionLength::TWO); + SetFunction(env, stringFuncPrototype, "split", BuiltinsString::Split, FunctionLength::TWO); + SetFunction(env, stringFuncPrototype, "startsWith", BuiltinsString::StartsWith, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "substring", BuiltinsString::Substring, FunctionLength::TWO); + SetFunction(env, stringFuncPrototype, "substr", BuiltinsString::SubStr, FunctionLength::TWO); + SetFunction(env, stringFuncPrototype, "toLowerCase", BuiltinsString::ToLowerCase, FunctionLength::ZERO); + SetFunction(env, stringFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), + BuiltinsString::ToString, FunctionLength::ZERO); + SetFunction(env, stringFuncPrototype, "toUpperCase", BuiltinsString::ToUpperCase, FunctionLength::ZERO); + SetFunction(env, stringFuncPrototype, "trim", BuiltinsString::Trim, FunctionLength::ZERO); + SetFunction(env, stringFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), + BuiltinsString::ValueOf, FunctionLength::ZERO); + SetFunctionAtSymbol(env, stringFuncPrototype, env->GetIteratorSymbol(), "[Symbol.iterator]", + BuiltinsString::GetStringIterator, FunctionLength::ZERO); + + // String method + SetFunction(env, stringFunction, "fromCharCode", BuiltinsString::FromCharCode, FunctionLength::ONE); + SetFunction(env, stringFunction, "fromCodePoint", BuiltinsString::FromCodePoint, FunctionLength::ONE); + SetFunction(env, stringFunction, "raw", BuiltinsString::Raw, FunctionLength::ONE); + + // String.prototype.length + JSHandle lengthGetter = CreateGetter(env, BuiltinsString::GetLength, "length", FunctionLength::ZERO); + JSHandle lengthKey(factory_->NewFromString("length")); + SetGetter(stringFuncPrototype, lengthKey, lengthGetter); + + env->SetStringFunction(thread_, stringFunction); +} + +void Builtins::InitializeStringIterator(const JSHandle &env, + const JSHandle &iteratorFuncDynclass) const +{ + // StringIterator.prototype + JSHandle strIterPrototype(factory_->NewJSObject(iteratorFuncDynclass)); + + // StringIterator.prototype_or_dynclass + JSHandle strIterFuncInstanceDynclass = factory_->NewEcmaDynClass( + JSStringIterator::SIZE, JSType::JS_STRING_ITERATOR, JSHandle(strIterPrototype)); + + JSHandle strIterFunction( + factory_->NewJSFunction(env, static_cast(nullptr), FunctionKind::BASE_CONSTRUCTOR)); + strIterFunction->SetFunctionPrototype(thread_, strIterFuncInstanceDynclass.GetTaggedValue()); + + SetFunction(env, strIterPrototype, "next", StringIterator::Next, FunctionLength::ZERO); + SetStringTagSymbol(env, strIterPrototype, "String Iterator"); + + env->SetStringIterator(thread_, strIterFunction); + env->SetStringIteratorPrototype(thread_, strIterPrototype); +} + +void Builtins::InitializeIterator(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Iterator.prototype + JSHandle iteratorPrototype = factory_->NewJSObject(objFuncDynclass); + // Iterator.prototype.next() + SetFunction(env, iteratorPrototype, "next", BuiltinsIterator::Next, FunctionLength::ONE); + // Iterator.prototype.return() + SetFunction(env, iteratorPrototype, "return", BuiltinsIterator::Return, FunctionLength::ONE); + // Iterator.prototype.throw() + SetFunction(env, iteratorPrototype, "throw", BuiltinsIterator::Throw, FunctionLength::ONE); + // %IteratorPrototype% [ @@iterator ] + SetFunctionAtSymbol(env, iteratorPrototype, env->GetIteratorSymbol(), "[Symbol.iterator]", + BuiltinsIterator::GetIteratorObj, FunctionLength::ZERO); + env->SetIteratorPrototype(thread_, iteratorPrototype); + + // Iterator.dynclass + JSHandle iteratorFuncDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_ITERATOR, JSHandle(iteratorPrototype)); + + InitializeForinIterator(env, iteratorFuncDynclass); + InitializeSetIterator(env, iteratorFuncDynclass); + InitializeMapIterator(env, iteratorFuncDynclass); + InitializeArrayIterator(env, iteratorFuncDynclass); + InitializeStringIterator(env, iteratorFuncDynclass); +} + +void Builtins::InitializeForinIterator(const JSHandle &env, + const JSHandle &iteratorFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Iterator.prototype + JSHandle forinIteratorPrototype = factory_->NewJSObject(iteratorFuncDynclass); + JSHandle dynclass = factory_->NewEcmaDynClass(JSForInIterator::SIZE, JSType::JS_FORIN_ITERATOR, + JSHandle(forinIteratorPrototype)); + + // Iterator.prototype.next() + SetFunction(env, forinIteratorPrototype, "next", JSForInIterator::Next, FunctionLength::ONE); + env->SetForinIteratorPrototype(thread_, forinIteratorPrototype); + env->SetForinIteratorClass(thread_, dynclass); +} + +void Builtins::InitializeSetIterator(const JSHandle &env, + const JSHandle &iteratorFuncDynclass) const +{ + // SetIterator.prototype + JSHandle setIteratorPrototype(factory_->NewJSObject(iteratorFuncDynclass)); + // Iterator.prototype.next() + SetFunction(env, setIteratorPrototype, "next", JSSetIterator::Next, FunctionLength::ZERO); + SetStringTagSymbol(env, setIteratorPrototype, "Set Iterator"); + env->SetSetIteratorPrototype(thread_, setIteratorPrototype); +} + +void Builtins::InitializeMapIterator(const JSHandle &env, + const JSHandle &iteratorFuncDynclass) const +{ + // MapIterator.prototype + JSHandle mapIteratorPrototype(factory_->NewJSObject(iteratorFuncDynclass)); + // Iterator.prototype.next() + SetFunction(env, mapIteratorPrototype, "next", JSMapIterator::Next, FunctionLength::ZERO); + SetStringTagSymbol(env, mapIteratorPrototype, "Map Iterator"); + env->SetMapIteratorPrototype(thread_, mapIteratorPrototype); +} +void Builtins::InitializeArrayIterator(const JSHandle &env, + const JSHandle &iteratorFuncDynclass) const +{ + // ArrayIterator.prototype + JSHandle arrayIteratorPrototype(factory_->NewJSObject(iteratorFuncDynclass)); + // Iterator.prototype.next() + SetFunction(env, arrayIteratorPrototype, "next", JSArrayIterator::Next, FunctionLength::ZERO); + SetStringTagSymbol(env, arrayIteratorPrototype, "Array Iterator"); + env->SetArrayIteratorPrototype(thread_, arrayIteratorPrototype); +} + +void Builtins::InitializeRegExp(const JSHandle &env) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // RegExp.prototype + JSHandle objFun = env->GetObjectFunction(); + JSHandle regPrototype = factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle regPrototypeValue(regPrototype); + + // RegExp.prototype_or_dynclass + JSHandle regexpFuncInstanceDynclass = factory_->CreateJSRegExpInstanceClass(regPrototypeValue); + + // RegExp = new Function() + JSHandle regexpFunction( + NewBuiltinConstructor(env, regPrototype, RegExp::RegExpConstructor, "RegExp", FunctionLength::TWO)); + + JSHandle(regexpFunction)->SetFunctionPrototype(thread_, regexpFuncInstanceDynclass.GetTaggedValue()); + + // RegExp.prototype method + SetFunction(env, regPrototype, "exec", RegExp::Exec, FunctionLength::ONE); + SetFunction(env, regPrototype, "test", RegExp::Test, FunctionLength::ONE); + SetFunction(env, regPrototype, thread_->GlobalConstants()->GetHandledToStringString(), RegExp::ToString, + FunctionLength::ZERO); + + JSHandle flagsGetter = CreateGetter(env, RegExp::GetFlags, "flags", FunctionLength::ZERO); + JSHandle flagsKey(factory_->NewFromString("flags")); + SetGetter(regPrototype, flagsKey, flagsGetter); + + JSHandle sourceGetter = CreateGetter(env, RegExp::GetSource, "source", FunctionLength::ZERO); + JSHandle sourceKey(factory_->NewFromString("source")); + SetGetter(regPrototype, sourceKey, sourceGetter); + + JSHandle globalGetter = CreateGetter(env, RegExp::GetGlobal, "global", FunctionLength::ZERO); + JSHandle globalKey(factory_->NewFromString("global")); + SetGetter(regPrototype, globalKey, globalGetter); + + JSHandle ignoreCaseGetter = + CreateGetter(env, RegExp::GetIgnoreCase, "ignoreCase", FunctionLength::ZERO); + JSHandle ignoreCaseKey(factory_->NewFromString("ignoreCase")); + SetGetter(regPrototype, ignoreCaseKey, ignoreCaseGetter); + + JSHandle multilineGetter = + CreateGetter(env, RegExp::GetMultiline, "multiline", FunctionLength::ZERO); + JSHandle multilineKey(factory_->NewFromString("multiline")); + SetGetter(regPrototype, multilineKey, multilineGetter); + + JSHandle dotAllGetter = CreateGetter(env, RegExp::GetDotAll, "dotAll", FunctionLength::ZERO); + JSHandle dotAllKey(factory_->NewFromString("dotAll")); + SetGetter(regPrototype, dotAllKey, dotAllGetter); + + JSHandle stickyGetter = CreateGetter(env, RegExp::GetSticky, "sticky", FunctionLength::ZERO); + JSHandle stickyKey(factory_->NewFromString("sticky")); + SetGetter(regPrototype, stickyKey, stickyGetter); + + JSHandle unicodeGetter = CreateGetter(env, RegExp::GetUnicode, "unicode", FunctionLength::ZERO); + JSHandle unicodeKey(factory_->NewFromString("unicode")); + SetGetter(regPrototype, unicodeKey, unicodeGetter); + + // Set RegExp [ @@species ] + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesGetter = + CreateGetter(env, BuiltinsMap::Species, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(JSHandle(regexpFunction), speciesSymbol, speciesGetter); + + // Set RegExp.prototype[@@split] + SetFunctionAtSymbol(env, regPrototype, env->GetSplitSymbol(), "[Symbol.split]", RegExp::Split, FunctionLength::TWO); + // Set RegExp.prototype[@@search] + SetFunctionAtSymbol(env, regPrototype, env->GetSearchSymbol(), "[Symbol.search]", RegExp::Search, + FunctionLength::ONE); + // Set RegExp.prototype[@@match] + SetFunctionAtSymbol(env, regPrototype, env->GetMatchSymbol(), "[Symbol.match]", RegExp::Match, FunctionLength::ONE); + // Set RegExp.prototype[@@replace] + SetFunctionAtSymbol(env, regPrototype, env->GetReplaceSymbol(), "[Symbol.replace]", RegExp::Replace, + FunctionLength::TWO); + + env->SetRegExpFunction(thread_, regexpFunction); + auto globalConst = const_cast(thread_->GlobalConstants()); + globalConst->SetConstant(ConstantIndex::JS_REGEXP_CLASS_INDEX, regexpFuncInstanceDynclass.GetTaggedValue()); +} + +void Builtins::InitializeArray(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Arraybase.prototype + JSHandle arrBaseFuncInstanceDynclass = factory_->CreateJSArrayInstanceClass(objFuncPrototypeVal); + + // Array.prototype + JSHandle arrFuncPrototype = factory_->NewJSObject(arrBaseFuncInstanceDynclass); + JSHandle::Cast(arrFuncPrototype)->SetLength(thread_, JSTaggedValue(FunctionLength::ZERO)); + auto accessor = thread_->GlobalConstants()->GetArrayLengthAccessor(); + JSArray::Cast(*arrFuncPrototype)->SetPropertyInlinedProps(thread_, JSArray::LENGTH_INLINE_PROPERTY_INDEX, accessor); + JSHandle arrFuncPrototypeValue(arrFuncPrototype); + + // Array.prototype_or_dynclass + JSHandle arrFuncInstanceDynclass = factory_->CreateJSArrayInstanceClass(arrFuncPrototypeValue); + + // Array = new Function() + JSHandle arrayFunction( + NewBuiltinConstructor(env, arrFuncPrototype, BuiltinsArray::ArrayConstructor, "Array", FunctionLength::ONE)); + JSHandle arrayFuncFunction(arrayFunction); + + // Set the [[Realm]] internal slot of F to the running execution context's Realm + JSHandle lexicalEnv = factory_->NewLexicalEnv(0); + lexicalEnv->SetParentEnv(thread_, env.GetTaggedValue()); + arrayFuncFunction->SetLexicalEnv(thread_, lexicalEnv.GetTaggedValue(), SKIP_BARRIER); + + arrayFuncFunction->SetFunctionPrototype(thread_, arrFuncInstanceDynclass.GetTaggedValue()); + + // Array.prototype method + SetFunction(env, arrFuncPrototype, "concat", BuiltinsArray::Concat, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "copyWithin", BuiltinsArray::CopyWithin, FunctionLength::TWO); + SetFunction(env, arrFuncPrototype, "entries", BuiltinsArray::Entries, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "every", BuiltinsArray::Every, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "fill", BuiltinsArray::Fill, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "filter", BuiltinsArray::Filter, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "find", BuiltinsArray::Find, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "findIndex", BuiltinsArray::FindIndex, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "forEach", BuiltinsArray::ForEach, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "indexOf", BuiltinsArray::IndexOf, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "join", BuiltinsArray::Join, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "keys", BuiltinsArray::Keys, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "lastIndexOf", BuiltinsArray::LastIndexOf, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "map", BuiltinsArray::Map, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "pop", BuiltinsArray::Pop, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "push", BuiltinsArray::Push, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "reduce", BuiltinsArray::Reduce, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "reduceRight", BuiltinsArray::ReduceRight, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "reverse", BuiltinsArray::Reverse, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "shift", BuiltinsArray::Shift, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "slice", BuiltinsArray::Slice, FunctionLength::TWO); + SetFunction(env, arrFuncPrototype, "some", BuiltinsArray::Some, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "sort", BuiltinsArray::Sort, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "splice", BuiltinsArray::Splice, FunctionLength::TWO); + SetFunction(env, arrFuncPrototype, thread_->GlobalConstants()->GetHandledToLocaleStringString(), + BuiltinsArray::ToLocaleString, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), BuiltinsArray::ToString, + FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "unshift", BuiltinsArray::Unshift, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "values", BuiltinsArray::Values, FunctionLength::ZERO); + + // %ArrayPrototype% [ @@iterator ] + JSHandle values(factory_->NewFromString("values")); + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle valuesFunc = + JSObject::GetMethod(thread_, JSHandle::Cast(arrFuncPrototype), values); + PropertyDescriptor iteartorDesc(thread_, valuesFunc, true, false, true); + JSObject::DefineOwnProperty(thread_, arrFuncPrototype, iteratorSymbol, iteartorDesc); + + // Array method + SetFunction(env, arrayFunction, "from", BuiltinsArray::From, FunctionLength::ONE); + SetFunction(env, arrayFunction, "isArray", BuiltinsArray::IsArray, FunctionLength::ONE); + SetFunction(env, arrayFunction, "of", BuiltinsArray::Of, FunctionLength::ZERO); + + // 22.1.2.5 get %Array% [ @@species ] + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesGetter = + CreateGetter(env, BuiltinsArray::Species, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(JSHandle(arrayFunction), speciesSymbol, speciesGetter); + + const int arrProtoLen = 0; + JSHandle key_string = thread_->GlobalConstants()->GetHandledLengthString(); + PropertyDescriptor descriptor(thread_, JSHandle(thread_, JSTaggedValue(arrProtoLen)), true, false, + false); + JSObject::DefineOwnProperty(thread_, arrFuncPrototype, key_string, descriptor); + + JSHandle valuesKey(factory_->NewFromString("values")); + PropertyDescriptor desc(thread_); + JSObject::GetOwnProperty(thread_, arrFuncPrototype, valuesKey, desc); + + // Array.prototype [ @@unscopables ] + JSHandle unscopablesSymbol = env->GetUnscopablesSymbol(); + JSHandle unscopablesGetter = + CreateGetter(env, BuiltinsArray::Unscopables, "[Symbol.unscopables]", FunctionLength::ZERO); + SetGetter(JSHandle(arrFuncPrototype), unscopablesSymbol, unscopablesGetter); + + env->SetArrayProtoValuesFunction(thread_, desc.GetValue()); + env->SetArrayFunction(thread_, arrayFunction); + env->SetArrayPrototype(thread_, arrFuncPrototype); +} + +void Builtins::InitializeTypedArray(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // TypedArray.prototype + JSHandle typedArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle typedArrFuncPrototypeValue(typedArrFuncPrototype); + + // TypedArray.prototype_or_dynclass + JSHandle typedArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::JSTypedArray::SIZE, JSType::JS_TYPED_ARRAY, typedArrFuncPrototypeValue); + + // TypedArray = new Function() + JSHandle typedArrayFunction(NewBuiltinConstructor( + env, typedArrFuncPrototype, BuiltinsTypedArray::TypedArrayBaseConstructor, "TypedArray", FunctionLength::ZERO)); + + JSHandle(typedArrayFunction) + ->SetProtoOrDynClass(thread_, typedArrFuncInstanceDynclass.GetTaggedValue()); + + // TypedArray.prototype method + SetFunction(env, typedArrFuncPrototype, "copyWithin", BuiltinsTypedArray::CopyWithin, FunctionLength::TWO); + SetFunction(env, typedArrFuncPrototype, "entries", BuiltinsTypedArray::Entries, FunctionLength::ZERO); + SetFunction(env, typedArrFuncPrototype, "every", BuiltinsTypedArray::Every, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "fill", BuiltinsTypedArray::Fill, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "filter", BuiltinsTypedArray::Filter, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "find", BuiltinsTypedArray::Find, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "findIndex", BuiltinsTypedArray::FindIndex, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "forEach", BuiltinsTypedArray::ForEach, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "indexOf", BuiltinsTypedArray::IndexOf, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "join", BuiltinsTypedArray::Join, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "keys", BuiltinsTypedArray::Keys, FunctionLength::ZERO); + SetFunction(env, typedArrFuncPrototype, "lastIndexOf", BuiltinsTypedArray::LastIndexOf, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "map", BuiltinsTypedArray::Map, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "reduce", BuiltinsTypedArray::Reduce, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "reduceRight", BuiltinsTypedArray::ReduceRight, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "reverse", BuiltinsTypedArray::Reverse, FunctionLength::ZERO); + SetFunction(env, typedArrFuncPrototype, "set", BuiltinsTypedArray::Set, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "slice", BuiltinsTypedArray::Slice, FunctionLength::TWO); + SetFunction(env, typedArrFuncPrototype, "some", BuiltinsTypedArray::Some, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "sort", BuiltinsTypedArray::Sort, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "subarray", BuiltinsTypedArray::Subarray, FunctionLength::TWO); + SetFunction(env, typedArrFuncPrototype, thread_->GlobalConstants()->GetHandledToLocaleStringString(), + BuiltinsTypedArray::ToLocaleString, FunctionLength::ZERO); + SetFunction(env, typedArrFuncPrototype, "values", BuiltinsTypedArray::Values, FunctionLength::ZERO); + + JSHandle bufferGetter = + CreateGetter(env, BuiltinsTypedArray::GetBuffer, "buffer", FunctionLength::ZERO); + JSHandle bufferKey(factory_->NewFromString("buffer")); + SetGetter(typedArrFuncPrototype, bufferKey, bufferGetter); + + JSHandle byteLengthGetter = + CreateGetter(env, BuiltinsTypedArray::GetByteLength, "byteLength", FunctionLength::ZERO); + JSHandle byteLengthKey(factory_->NewFromString("byteLength")); + SetGetter(typedArrFuncPrototype, byteLengthKey, byteLengthGetter); + + JSHandle byteOffsetGetter = + CreateGetter(env, BuiltinsTypedArray::GetByteOffset, "byteOffset", FunctionLength::ZERO); + JSHandle byteOffsetKey(factory_->NewFromString("byteOffset")); + SetGetter(typedArrFuncPrototype, byteOffsetKey, byteOffsetGetter); + + JSHandle lengthGetter = + CreateGetter(env, BuiltinsTypedArray::GetLength, "length", FunctionLength::ZERO); + JSHandle lengthKey(factory_->NewFromString("length")); + SetGetter(typedArrFuncPrototype, lengthKey, lengthGetter); + + // %TypedArray%.prototype.toString() + JSHandle arrFuncPrototype = env->GetArrayPrototype(); + JSHandle toStringFunc = + JSObject::GetMethod(thread_, arrFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString()); + PropertyDescriptor toStringDesc(thread_, toStringFunc, true, false, true); + JSObject::DefineOwnProperty(thread_, typedArrFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), + toStringDesc); + + // %TypedArray%.prototype [ @@iterator ] ( ) + JSHandle values(factory_->NewFromString("values")); + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle valuesFunc = + JSObject::GetMethod(thread_, JSHandle::Cast(typedArrFuncPrototype), values); + PropertyDescriptor iteartorDesc(thread_, valuesFunc, true, false, true); + JSObject::DefineOwnProperty(thread_, typedArrFuncPrototype, iteratorSymbol, iteartorDesc); + + // 22.2.3.31 get %TypedArray%.prototype [ @@toStringTag ] + JSHandle toStringTagSymbol = env->GetToStringTagSymbol(); + JSHandle toStringTagGetter = + CreateGetter(env, BuiltinsTypedArray::ToStringTag, "[Symbol.toStringTag]", FunctionLength::ZERO); + SetGetter(typedArrFuncPrototype, toStringTagSymbol, toStringTagGetter); + + // TypedArray method + SetFunction(env, typedArrayFunction, "from", BuiltinsTypedArray::From, FunctionLength::ONE); + SetFunction(env, typedArrayFunction, "of", BuiltinsTypedArray::Of, FunctionLength::ZERO); + + // 22.2.2.4 get %TypedArray% [ @@species ] + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesGetter = + CreateGetter(env, BuiltinsTypedArray::Species, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(JSHandle(typedArrayFunction), speciesSymbol, speciesGetter); + + env->SetTypedArrayFunction(thread_, typedArrayFunction.GetTaggedValue()); + env->SetTypedArrayPrototype(thread_, typedArrFuncPrototype); + + JSHandle specificTypedArrayFuncClass = + factory_->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, env->GetTypedArrayFunction()); + specificTypedArrayFuncClass->SetConstructor(true); + JSHandle function = JSHandle::Cast(factory_->NewJSObject(specificTypedArrayFuncClass)); + function->SetBuiltinsCtorMode(); + env->SetSpecificTypedArrayFunctionClass(thread_, specificTypedArrayFuncClass); + + InitializeInt8Array(env, typedArrFuncInstanceDynclass); + InitializeUint8Array(env, typedArrFuncInstanceDynclass); + InitializeUint8ClampedArray(env, typedArrFuncInstanceDynclass); + InitializeInt16Array(env, typedArrFuncInstanceDynclass); + InitializeUint16Array(env, typedArrFuncInstanceDynclass); + InitializeInt32Array(env, typedArrFuncInstanceDynclass); + InitializeUint32Array(env, typedArrFuncInstanceDynclass); + InitializeFloat32Array(env, typedArrFuncInstanceDynclass); + InitializeFloat64Array(env, typedArrFuncInstanceDynclass); +} + +void Builtins::InitializeInt8Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Int8Array.prototype + JSHandle int8ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle int8ArrFuncPrototypeValue(int8ArrFuncPrototype); + + // Int8Array.prototype_or_dynclass + JSHandle int8ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::JSInt8Array::SIZE, JSType::JS_INT8_ARRAY, int8ArrFuncPrototypeValue); + + // Int8Array = new Function() + JSHandle int8ArrayFunction = factory_->NewSpecificTypedArrayFunction( + env, reinterpret_cast(BuiltinsTypedArray::Int8ArrayConstructor)); + InitializeCtor(env, int8ArrFuncPrototype, int8ArrayFunction, "Int8Array", FunctionLength::THREE); + + int8ArrayFunction->SetProtoOrDynClass(thread_, int8ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 1; + SetConstant(int8ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(int8ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetInt8ArrayFunction(thread_, int8ArrayFunction); +} + +void Builtins::InitializeUint8Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Uint8Array.prototype + JSHandle uint8ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle uint8ArrFuncPrototypeValue(uint8ArrFuncPrototype); + + // Uint8Array.prototype_or_dynclass + JSHandle uint8ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::JSUint8Array::SIZE, JSType::JS_UINT8_ARRAY, uint8ArrFuncPrototypeValue); + + // Uint8Array = new Function() + JSHandle uint8ArrayFunction = factory_->NewSpecificTypedArrayFunction( + env, reinterpret_cast(BuiltinsTypedArray::Uint8ArrayConstructor)); + InitializeCtor(env, uint8ArrFuncPrototype, uint8ArrayFunction, "Uint8Array", FunctionLength::THREE); + + uint8ArrayFunction->SetProtoOrDynClass(thread_, uint8ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 1; + SetConstant(uint8ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(uint8ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetUint8ArrayFunction(thread_, uint8ArrayFunction); +} + +void Builtins::InitializeUint8ClampedArray(const JSHandle &env, + const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Uint8ClampedArray.prototype + JSHandle uint8ClampedArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle uint8ClampedArrFuncPrototypeValue(uint8ClampedArrFuncPrototype); + + // Uint8ClampedArray.prototype_or_dynclass + JSHandle uint8ClampedArrFuncInstanceDynclass = + factory_->NewEcmaDynClass(panda::ecmascript::JSUint8ClampedArray::SIZE, JSType::JS_UINT8_CLAMPED_ARRAY, + uint8ClampedArrFuncPrototypeValue); + + // Uint8ClampedArray = new Function() + JSHandle uint8ClampedArrayFunction = factory_->NewSpecificTypedArrayFunction( + env, reinterpret_cast(BuiltinsTypedArray::Uint8ClampedArrayConstructor)); + InitializeCtor(env, uint8ClampedArrFuncPrototype, uint8ClampedArrayFunction, "Uint8ClampedArray", + FunctionLength::THREE); + + uint8ClampedArrayFunction->SetProtoOrDynClass(thread_, uint8ClampedArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 1; + SetConstant(uint8ClampedArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(uint8ClampedArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetUint8ClampedArrayFunction(thread_, uint8ClampedArrayFunction); +} + +void Builtins::InitializeInt16Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Int16Array.prototype + JSHandle int16ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle int16ArrFuncPrototypeValue(int16ArrFuncPrototype); + + // Int16Array.prototype_or_dynclass + JSHandle int16ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::JSInt16Array::SIZE, JSType::JS_INT16_ARRAY, int16ArrFuncPrototypeValue); + + // Int16Array = new Function() + JSHandle int16ArrayFunction = factory_->NewSpecificTypedArrayFunction( + env, reinterpret_cast(BuiltinsTypedArray::Int16ArrayConstructor)); + InitializeCtor(env, int16ArrFuncPrototype, int16ArrayFunction, "Int16Array", FunctionLength::THREE); + + int16ArrayFunction->SetProtoOrDynClass(thread_, int16ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 2; + SetConstant(int16ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(int16ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetInt16ArrayFunction(thread_, int16ArrayFunction); +} + +void Builtins::InitializeUint16Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Uint16Array.prototype + JSHandle uint16ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle uint16ArrFuncPrototypeValue(uint16ArrFuncPrototype); + + // Uint16Array.prototype_or_dynclass + JSHandle uint16ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::JSUint16Array::SIZE, JSType::JS_UINT16_ARRAY, uint16ArrFuncPrototypeValue); + + // Uint16Array = new Function() + JSHandle uint16ArrayFunction = factory_->NewSpecificTypedArrayFunction( + env, reinterpret_cast(BuiltinsTypedArray::Uint16ArrayConstructor)); + InitializeCtor(env, uint16ArrFuncPrototype, uint16ArrayFunction, "Uint16Array", FunctionLength::THREE); + + uint16ArrayFunction->SetProtoOrDynClass(thread_, uint16ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 2; + SetConstant(uint16ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(uint16ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetUint16ArrayFunction(thread_, uint16ArrayFunction); +} + +void Builtins::InitializeInt32Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Int32Array.prototype + JSHandle int32ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle int32ArrFuncPrototypeValue(int32ArrFuncPrototype); + + // Int32Array.prototype_or_dynclass + JSHandle int32ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::JSInt32Array::SIZE, JSType::JS_INT32_ARRAY, int32ArrFuncPrototypeValue); + + // Int32Array = new Function() + JSHandle int32ArrayFunction = factory_->NewSpecificTypedArrayFunction( + env, reinterpret_cast(BuiltinsTypedArray::Int32ArrayConstructor)); + InitializeCtor(env, int32ArrFuncPrototype, int32ArrayFunction, "Int32Array", FunctionLength::THREE); + + int32ArrayFunction->SetProtoOrDynClass(thread_, int32ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 4; + SetConstant(int32ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(int32ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetInt32ArrayFunction(thread_, int32ArrayFunction); +} + +void Builtins::InitializeUint32Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Uint32Array.prototype + JSHandle uint32ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle uint32ArrFuncPrototypeValue(uint32ArrFuncPrototype); + + // Uint32Array.prototype_or_dynclass + JSHandle uint32ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::JSUint32Array::SIZE, JSType::JS_UINT32_ARRAY, uint32ArrFuncPrototypeValue); + + // Uint32Array = new Function() + JSHandle uint32ArrayFunction = factory_->NewSpecificTypedArrayFunction( + env, reinterpret_cast(BuiltinsTypedArray::Uint32ArrayConstructor)); + InitializeCtor(env, uint32ArrFuncPrototype, uint32ArrayFunction, "Uint32Array", FunctionLength::THREE); + + uint32ArrayFunction->SetProtoOrDynClass(thread_, uint32ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 4; + SetConstant(uint32ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(uint32ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetUint32ArrayFunction(thread_, uint32ArrayFunction); +} + +void Builtins::InitializeFloat32Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Float32Array.prototype + JSHandle float32ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle float32ArrFuncPrototypeValue(float32ArrFuncPrototype); + + // Float32Array.prototype_or_dynclass + JSHandle float32ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::JSFloat32Array::SIZE, JSType::JS_FLOAT32_ARRAY, float32ArrFuncPrototypeValue); + + // Float32Array = new Function() + JSHandle float32ArrayFunction = factory_->NewSpecificTypedArrayFunction( + env, reinterpret_cast(BuiltinsTypedArray::Float32ArrayConstructor)); + InitializeCtor(env, float32ArrFuncPrototype, float32ArrayFunction, "Float32Array", FunctionLength::THREE); + + float32ArrayFunction->SetProtoOrDynClass(thread_, float32ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 4; + SetConstant(float32ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(float32ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetFloat32ArrayFunction(thread_, float32ArrayFunction); +} + +void Builtins::InitializeFloat64Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Float64Array.prototype + JSHandle float64ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle float64ArrFuncPrototypeValue(float64ArrFuncPrototype); + + // Float64Array.prototype_or_dynclass + JSHandle float64ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::JSFloat64Array::SIZE, JSType::JS_FLOAT64_ARRAY, float64ArrFuncPrototypeValue); + + // Float64Array = new Function() + JSHandle float64ArrayFunction = factory_->NewSpecificTypedArrayFunction( + env, reinterpret_cast(BuiltinsTypedArray::Float64ArrayConstructor)); + InitializeCtor(env, float64ArrFuncPrototype, float64ArrayFunction, "Float64Array", FunctionLength::THREE); + + float64ArrayFunction->SetProtoOrDynClass(thread_, float64ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 8; + SetConstant(float64ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(float64ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetFloat64ArrayFunction(thread_, float64ArrayFunction); +} + +void Builtins::InitializeArrayBuffer(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // ArrayBuffer.prototype + JSHandle arrayBufferFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle arrayBufferFuncPrototypeValue(arrayBufferFuncPrototype); + + // ArrayBuffer.prototype_or_dynclass + JSHandle arrayBufferFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSArrayBuffer::SIZE, JSType::JS_ARRAY_BUFFER, arrayBufferFuncPrototypeValue); + + // ArrayBuffer = new Function() + JSHandle arrayBufferFunction(NewBuiltinConstructor( + env, arrayBufferFuncPrototype, ArrayBuffer::ArrayBufferConstructor, "ArrayBuffer", FunctionLength::ONE)); + + JSHandle(arrayBufferFunction) + ->SetFunctionPrototype(thread_, arrayBufferFuncInstanceDynclass.GetTaggedValue()); + + // ArrayBuffer prototype method + SetFunction(env, arrayBufferFuncPrototype, "slice", ArrayBuffer::Slice, FunctionLength::TWO); + + // ArrayBuffer method + SetFunction(env, arrayBufferFunction, "isView", ArrayBuffer::IsView, FunctionLength::ONE); + + // 24.1.3.3 get ArrayBuffer[@@species] + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesGetter = + CreateGetter(env, ArrayBuffer::Species, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(JSHandle(arrayBufferFunction), speciesSymbol, speciesGetter); + + // 24.1.4.1 get ArrayBuffer.prototype.byteLength + JSHandle lengthGetter = + CreateGetter(env, ArrayBuffer::GetByteLength, "byteLength", FunctionLength::ZERO); + JSHandle lengthKey(factory_->NewFromString("byteLength")); + SetGetter(arrayBufferFuncPrototype, lengthKey, lengthGetter); + + // 24.1.4.4 ArrayBuffer.prototype[@@toStringTag] + SetStringTagSymbol(env, arrayBufferFuncPrototype, "ArrayBuffer"); + + env->SetArrayBufferFunction(thread_, arrayBufferFunction.GetTaggedValue()); +} + +void Builtins::InitializeReflect(const JSHandle &env, + const JSHandle &objFuncPrototypeVal) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle reflectDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, objFuncPrototypeVal); + JSHandle reflectObject = factory_->NewJSObject(reflectDynclass); + + SetFunction(env, reflectObject, "apply", Reflect::ReflectApply, FunctionLength::THREE); + SetFunction(env, reflectObject, "construct", Reflect::ReflectConstruct, FunctionLength::TWO); + SetFunction(env, reflectObject, "defineProperty", Reflect::ReflectDefineProperty, FunctionLength::THREE); + SetFunction(env, reflectObject, "deleteProperty", Reflect::ReflectDeleteProperty, FunctionLength::TWO); + SetFunction(env, reflectObject, "get", Reflect::ReflectGet, FunctionLength::TWO); + SetFunction(env, reflectObject, "getOwnPropertyDescriptor", Reflect::ReflectGetOwnPropertyDescriptor, + FunctionLength::TWO); + SetFunction(env, reflectObject, "getPrototypeOf", Reflect::ReflectGetPrototypeOf, FunctionLength::ONE); + SetFunction(env, reflectObject, "has", Reflect::ReflectHas, FunctionLength::TWO); + SetFunction(env, reflectObject, "isExtensible", Reflect::ReflectIsExtensible, FunctionLength::ONE); + SetFunction(env, reflectObject, "ownKeys", Reflect::ReflectOwnKeys, FunctionLength::ONE); + SetFunction(env, reflectObject, "preventExtensions", Reflect::ReflectPreventExtensions, FunctionLength::ONE); + SetFunction(env, reflectObject, "set", Reflect::ReflectSet, FunctionLength::THREE); + SetFunction(env, reflectObject, "setPrototypeOf", Reflect::ReflectSetPrototypeOf, FunctionLength::TWO); + + JSHandle reflectString(factory_->NewFromString("Reflect")); + JSHandle globalObject(thread_, env->GetGlobalObject()); + PropertyDescriptor reflectDesc(thread_, JSHandle::Cast(reflectObject), true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, reflectString, reflectDesc); + + // @@ToStringTag + SetStringTagSymbol(env, reflectObject, "Reflect"); + + env->SetReflectFunction(thread_, reflectObject.GetTaggedValue()); +} + +void Builtins::InitializePromise(const JSHandle &env, const JSHandle &promiseFuncDynclass) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Promise.prototype + JSHandle promiseFuncPrototype = factory_->NewJSObject(promiseFuncDynclass); + JSHandle promiseFuncPrototypeValue(promiseFuncPrototype); + // Promise.prototype_or_dynclass + JSHandle promiseFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSPromise::SIZE, JSType::JS_PROMISE, promiseFuncPrototypeValue); + // Promise() = new Function() + JSHandle promiseFunction( + NewBuiltinConstructor(env, promiseFuncPrototype, Promise::PromiseConstructor, "Promise", FunctionLength::ONE)); + JSHandle(promiseFunction)->SetFunctionPrototype(thread_, promiseFuncInstanceDynclass.GetTaggedValue()); + + // Promise method + SetFunction(env, promiseFunction, "all", Promise::All, FunctionLength::ONE); + SetFunction(env, promiseFunction, "race", Promise::Race, FunctionLength::ONE); + SetFunction(env, promiseFunction, "resolve", Promise::Resolve, FunctionLength::ONE); + SetFunction(env, promiseFunction, "reject", Promise::Reject, FunctionLength::ONE); + + // promise.prototype method + SetFunction(env, promiseFuncPrototype, "catch", Promise::Catch, FunctionLength::ONE); + SetFunction(env, promiseFuncPrototype, "then", Promise::Then, FunctionLength::TWO); + + // Promise.prototype [ @@toStringTag ] + SetStringTagSymbol(env, promiseFuncPrototype, "Promise"); + + // Set Promise [@@species] + JSHandle speciesSymbol(env->GetSpeciesSymbol()); + JSHandle speciesGetter = + CreateGetter(env, Promise::GetSpecies, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(promiseFunction, speciesSymbol, speciesGetter); + + env->SetPromiseFunction(thread_, promiseFunction); +} + +void Builtins::InitializePromiseJob(const JSHandle &env) +{ + JSHandle keyString(thread_->GlobalConstants()->GetHandledEmptyString()); + auto func = NewFunction(env, keyString, BuiltinsPromiseJob::PromiseReactionJob, FunctionLength::TWO); + env->SetPromiseReactionJob(thread_, func); + func = NewFunction(env, keyString, BuiltinsPromiseJob::PromiseResolveThenableJob, FunctionLength::THREE); + env->SetPromiseResolveThenableJob(thread_, func); +} + +void Builtins::InitializeDataView(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // ArrayBuffer.prototype + JSHandle dataViewFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle dataViewFuncPrototypeValue(dataViewFuncPrototype); + + // ArrayBuffer.prototype_or_dynclass + JSHandle dataViewFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSDataView::SIZE, JSType::JS_DATA_VIEW, dataViewFuncPrototypeValue); + + // ArrayBuffer = new Function() + JSHandle dataViewFunction(NewBuiltinConstructor(env, dataViewFuncPrototype, DataView::DataViewConstructor, + "DataView", FunctionLength::ONE)); + + JSHandle(dataViewFunction)->SetProtoOrDynClass(thread_, dataViewFuncInstanceDynclass.GetTaggedValue()); + // DataView.prototype method + SetFunction(env, dataViewFuncPrototype, "getFloat32", DataView::GetFloat32, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getFloat64", DataView::GetFloat64, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getInt8", DataView::GetInt8, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getInt16", DataView::GetInt16, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getInt32", DataView::GetInt32, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getUint8", DataView::GetUint8, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getUint16", DataView::GetUint16, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getUint32", DataView::GetUint32, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "setFloat32", DataView::SetFloat32, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setFloat64", DataView::SetFloat64, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setInt8", DataView::SetInt8, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setInt16", DataView::SetInt16, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setInt32", DataView::SetInt32, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setUint8", DataView::SetUint8, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setUint16", DataView::SetUint16, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setUint32", DataView::SetUint32, FunctionLength::TWO); + + // 24.2.4.1 get DataView.prototype.buffer + JSHandle bufferGetter = CreateGetter(env, DataView::GetBuffer, "buffer", FunctionLength::ZERO); + JSHandle bufferKey(factory_->NewFromString("buffer")); + SetGetter(dataViewFuncPrototype, bufferKey, bufferGetter); + + // 24.2.4.2 get DataView.prototype.byteLength + JSHandle lengthGetter = + CreateGetter(env, DataView::GetByteLength, "byteLength", FunctionLength::ZERO); + JSHandle lengthKey(factory_->NewFromString("byteLength")); + SetGetter(dataViewFuncPrototype, lengthKey, lengthGetter); + + // 24.2.4.3 get DataView.prototype.byteOffset + JSHandle offsetGetter = CreateGetter(env, DataView::GetOffset, "byteOffset", FunctionLength::ZERO); + JSHandle offsetKey(factory_->NewFromString("byteOffset")); + SetGetter(dataViewFuncPrototype, offsetKey, offsetGetter); + + // 24.2.4.21 DataView.prototype[ @@toStringTag ] + SetStringTagSymbol(env, dataViewFuncPrototype, "DataView"); + env->SetDataViewFunction(thread_, dataViewFunction.GetTaggedValue()); +} + +JSHandle Builtins::NewBuiltinConstructor(const JSHandle &env, + const JSHandle &prototype, EcmaEntrypoint ctorFunc, + const char *name, int length) const +{ + JSHandle ctor = + factory_->NewJSFunction(env, reinterpret_cast(ctorFunc), FunctionKind::BUILTIN_CONSTRUCTOR); + InitializeCtor(env, prototype, ctor, name, length); + return ctor; +} + +JSHandle Builtins::NewFunction(const JSHandle &env, const JSHandle &key, + EcmaEntrypoint func, int length) const +{ + JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); + JSHandle baseFunction(function); + JSHandle handleUndefine(thread_, JSTaggedValue::Undefined()); + JSFunction::SetFunctionName(thread_, baseFunction, key, handleUndefine); + return function; +} + +void Builtins::SetFunction(const JSHandle &env, const JSHandle &obj, const char *key, + EcmaEntrypoint func, int length) const +{ + JSHandle keyString(factory_->NewFromString(key)); + SetFunction(env, obj, keyString, func, length); +} + +void Builtins::SetFunction(const JSHandle &env, const JSHandle &obj, + const JSHandle &key, EcmaEntrypoint func, int length) const +{ + JSHandle function(NewFunction(env, key, func, length)); + PropertyDescriptor descriptor(thread_, JSHandle(function), true, false, true); + JSObject::DefineOwnProperty(thread_, obj, key, descriptor); +} + +template +void Builtins::SetFunctionAtSymbol(const JSHandle &env, const JSHandle &obj, + const JSHandle &symbol, const char *name, EcmaEntrypoint func, + int length) const +{ + JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); + JSHandle nameString(factory_->NewFromString(name)); + JSHandle baseFunction(function); + JSHandle handleUndefine(thread_, JSTaggedValue::Undefined()); + JSFunction::SetFunctionName(thread_, baseFunction, nameString, handleUndefine); + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + if constexpr (flag == JSSymbol::SYMBOL_TO_PRIMITIVE_TYPE) { + PropertyDescriptor descriptor(thread_, JSHandle::Cast(function), false, false, true); + JSObject::DefineOwnProperty(thread_, obj, symbol, descriptor); + return; + } else if constexpr (flag == JSSymbol::SYMBOL_HAS_INSTANCE_TYPE) { // NOLINTE(readability-braces-around-statements) + // ecma 19.2.3.6 Function.prototype[@@hasInstance] has the attributes + // { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }. + PropertyDescriptor descriptor(thread_, JSHandle::Cast(function), false, false, false); + JSObject::DefineOwnProperty(thread_, obj, symbol, descriptor); + return; + } + PropertyDescriptor descriptor(thread_, JSHandle::Cast(function), true, false, true); + JSObject::DefineOwnProperty(thread_, obj, symbol, descriptor); +} + +void Builtins::SetStringTagSymbol(const JSHandle &env, const JSHandle &obj, const char *key) const +{ + JSHandle tag(factory_->NewFromString(key)); + JSHandle symbol = env->GetToStringTagSymbol(); + PropertyDescriptor desc(thread_, tag, false, false, true); + JSObject::DefineOwnProperty(thread_, obj, symbol, desc); +} + +JSHandle Builtins::CreateGetter(const JSHandle &env, EcmaEntrypoint func, const char *name, + int length) const +{ + JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); + JSHandle funcName(factory_->NewFromString(name)); + JSHandle prefix = thread_->GlobalConstants()->GetHandledGetString(); + JSFunction::SetFunctionName(thread_, JSHandle(function), funcName, prefix); + return JSHandle(function); +} + +JSHandle Builtins::CreateSetter(const JSHandle &env, EcmaEntrypoint func, const char *name, + int length) +{ + JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); + JSHandle funcName(factory_->NewFromString(name)); + JSHandle prefix = thread_->GlobalConstants()->GetHandledSetString(); + JSFunction::SetFunctionName(thread_, JSHandle(function), funcName, prefix); + return JSHandle(function); +} + +void Builtins::SetConstant(const JSHandle &obj, const char *key, JSTaggedValue value) const +{ + JSHandle keyString(factory_->NewFromString(key)); + PropertyDescriptor descriptor(thread_, JSHandle(thread_, value), false, false, false); + JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); +} +void Builtins::SetGlobalThis(const JSHandle &obj, const char *key, const JSHandle &globalValue) +{ + JSHandle keyString(factory_->NewFromString(key)); + PropertyDescriptor descriptor(thread_, globalValue, true, false, true); + JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); +} + +void Builtins::SetAttribute(const JSHandle &obj, const char *key, const char *value) const +{ + JSHandle keyString(factory_->NewFromString(key)); + PropertyDescriptor descriptor(thread_, JSHandle(factory_->NewFromString(value)), true, false, true); + JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); +} + +void Builtins::SetNoneAttributeProperty(const JSHandle &obj, const char *key, + const JSHandle &value) const +{ + JSHandle keyString(factory_->NewFromString(key)); + PropertyDescriptor des(thread_, value, false, false, false); + JSObject::DefineOwnProperty(thread_, obj, keyString, des); +} + +void Builtins::SetFuncToObjAndGlobal(const JSHandle &env, const JSHandle &globalObject, + const JSHandle &obj, const char *key, EcmaEntrypoint func, int length) +{ + JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); + JSHandle keyString(factory_->NewFromString(key)); + JSHandle baseFunction(function); + JSHandle handleUndefine(thread_, JSTaggedValue::Undefined()); + JSFunction::SetFunctionName(thread_, baseFunction, keyString, handleUndefine); + PropertyDescriptor descriptor(thread_, JSHandle::Cast(function), true, false, true); + JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); + JSObject::DefineOwnProperty(thread_, globalObject, keyString, descriptor); +} + +void Builtins::StrictModeForbiddenAccessCallerArguments(const JSHandle &env, + const JSHandle &prototype) const +{ + JSHandle function = + factory_->NewJSFunction(env, reinterpret_cast(JSFunction::AccessCallerArgumentsThrowTypeError)); + + JSHandle caller(factory_->NewFromString("caller")); + SetAccessor(prototype, caller, JSHandle::Cast(function), JSHandle::Cast(function)); + + JSHandle arguments(factory_->NewFromString("arguments")); + SetAccessor(prototype, arguments, JSHandle::Cast(function), JSHandle::Cast(function)); +} + +void Builtins::InitializeGeneratorFunction(const JSHandle &env, + const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle generatorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle generatorFuncPrototypeValue(generatorFuncPrototype); + + // 26.3.3.1 GeneratorFunction.prototype.constructor + // GeneratorFunction.prototype_or_dynclass + JSHandle generatorFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_GENERATOR_FUNCTION, generatorFuncPrototypeValue); + generatorFuncInstanceDynclass->SetCallable(true); + generatorFuncInstanceDynclass->SetExtensible(true); + // GeneratorFunction = new GeneratorFunction() + JSHandle generatorFunction = + NewBuiltinConstructor(env, generatorFuncPrototype, GeneratorObject::GeneratorFunctionConstructor, + "GeneratorFunction", FunctionLength::ONE); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor generatorDesc(thread_, JSHandle::Cast(generatorFunction), false, false, true); + JSObject::DefineOwnProperty(thread_, generatorFuncPrototype, constructorKey, generatorDesc); + generatorFunction->SetProtoOrDynClass(thread_, generatorFuncInstanceDynclass.GetTaggedValue()); + env->SetGeneratorFunctionFunction(thread_, generatorFunction); + + // 26.3.3.2 GeneratorFunction.prototype.prototype -> Generator prototype object. + PropertyDescriptor descriptor(thread_, env->GetGeneratorPrototype(), false, false, true); + JSObject::DefineOwnProperty(thread_, generatorFuncPrototype, globalConst->GetHandledPrototypeString(), descriptor); + + // 26.3.3.3 GeneratorFunction.prototype[@@toStringTag] + SetStringTagSymbol(env, generatorFuncPrototype, "GeneratorFunction"); + + // GeneratorFunction prototype __proto__ -> Function. + JSObject::SetPrototype(thread_, generatorFuncPrototype, env->GetFunctionPrototype()); + + // 26.5.1.1 Generator.prototype.constructor -> %GeneratorFunction.prototype%. + PropertyDescriptor generatorObjDesc(thread_, generatorFuncPrototypeValue, false, false, true); + JSObject::DefineOwnProperty(thread_, JSHandle(env->GetInitialGenerator()), + globalConst->GetHandledConstructorString(), generatorObjDesc); + + // Generator instances prototype -> GeneratorFunction.prototype.prototype + PropertyDescriptor generatorObjProtoDesc(thread_, generatorFuncPrototypeValue, true, false, false); + JSObject::DefineOwnProperty(thread_, JSHandle(env->GetInitialGenerator()), + globalConst->GetHandledPrototypeString(), generatorObjProtoDesc); + + env->SetGeneratorFunctionPrototype(thread_, generatorFuncPrototype); +} + +void Builtins::InitializeGenerator(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle generatorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + + // GeneratorObject.prototype method + // 26.5.1.2 Generator.prototype.next(value) + SetFunction(env, generatorFuncPrototype, "next", GeneratorObject::GeneratorPrototypeNext, FunctionLength::ONE); + // 26.5.1.3 Generator.prototype.return(value) + SetFunction(env, generatorFuncPrototype, "return", GeneratorObject::GeneratorPrototypeReturn, FunctionLength::ONE); + // 26.5.1.4 Generator.prototype.throw(exception) + SetFunction(env, generatorFuncPrototype, "throw", GeneratorObject::GeneratorPrototypeThrow, FunctionLength::ONE); + + // 26.5.1.5 Generator.prototype[@@toStringTag] + SetStringTagSymbol(env, generatorFuncPrototype, "Generator"); + + // Generator with constructor, symbolTag, next/return/throw etc. + PropertyDescriptor descriptor(thread_, env->GetIteratorPrototype(), true, false, false); + JSObject::DefineOwnProperty(thread_, generatorFuncPrototype, globalConst->GetHandledPrototypeString(), descriptor); + env->SetGeneratorPrototype(thread_, generatorFuncPrototype); + JSObject::SetPrototype(thread_, generatorFuncPrototype, env->GetIteratorPrototype()); + + // Generator {} + JSHandle initialGeneratorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSObject::SetPrototype(thread_, initialGeneratorFuncPrototype, JSHandle(generatorFuncPrototype)); + env->SetInitialGenerator(thread_, initialGeneratorFuncPrototype); +} + +void Builtins::SetArgumentsSharedAccessor(const JSHandle &env) +{ + JSHandle throwFunction = env->GetThrowTypeError(); + + JSHandle accessor = factory_->NewAccessorData(); + accessor->SetGetter(thread_, throwFunction); + accessor->SetSetter(thread_, throwFunction); + env->SetArgumentsCallerAccessor(thread_, accessor); + + accessor = factory_->NewAccessorData(); + accessor->SetGetter(thread_, throwFunction); + accessor->SetSetter(thread_, throwFunction); + env->SetArgumentsCalleeAccessor(thread_, accessor); +} + +void Builtins::SetAccessor(const JSHandle &obj, const JSHandle &key, + const JSHandle &getter, const JSHandle &setter) const +{ + JSHandle accessor = factory_->NewAccessorData(); + accessor->SetGetter(thread_, getter); + accessor->SetSetter(thread_, setter); + PropertyAttributes attr = PropertyAttributes::DefaultAccessor(false, false, true); + JSObject::AddAccessor(thread_, JSHandle::Cast(obj), key, accessor, attr); +} + +void Builtins::SetGetter(const JSHandle &obj, const JSHandle &key, + const JSHandle &getter) const +{ + JSHandle accessor = factory_->NewAccessorData(); + accessor->SetGetter(thread_, getter); + PropertyAttributes attr = PropertyAttributes::DefaultAccessor(false, false, true); + JSObject::AddAccessor(thread_, JSHandle::Cast(obj), key, accessor, attr); +} + +void Builtins::InitializeJSNativeObject(const JSHandle &env) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle dynclass = factory_->NewEcmaDynClass(JSNativeObject::SIZE, JSType::JS_NATIVE_OBJECT, + JSHandle(thread_, JSTaggedValue::Null())); + + env->SetJSNativeObjectClass(thread_, dynclass); +} +} // namespace panda::ecmascript diff --git a/ecmascript/builtins.h b/ecmascript/builtins.h new file mode 100644 index 0000000000000000000000000000000000000000..4678867ec542bc79947a1c02c52ed0db8fe57f7e --- /dev/null +++ b/ecmascript/builtins.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMA_INIT_H +#define PANDA_RUNTIME_ECMA_INIT_H + +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "object_factory.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +struct ErrorParameter { + EcmaEntrypoint nativeConstructor{nullptr}; + EcmaEntrypoint nativeMethod{nullptr}; + const char *nativePropertyName{nullptr}; + JSType nativeJstype{JSType::INVALID}; +}; + +enum FunctionLength : uint8_t { ZERO = 0, ONE, TWO, THREE, FOUR }; + +class Builtins { +public: + explicit Builtins() = default; + ~Builtins() = default; + NO_COPY_SEMANTIC(Builtins); + NO_MOVE_SEMANTIC(Builtins); + + void Initialize(const JSHandle &env, JSThread *thread); + +private: + JSThread *thread_{nullptr}; + ObjectFactory *factory_{nullptr}; + EcmaVM *vm_{nullptr}; + + JSHandle NewBuiltinConstructor(const JSHandle &env, const JSHandle &prototype, + EcmaEntrypoint ctorFunc, const char *name, int length) const; + + JSHandle NewFunction(const JSHandle &env, const JSHandle &key, + EcmaEntrypoint func, int length) const; + + void InitializeCtor(const JSHandle &env, const JSHandle &prototype, + const JSHandle &ctor, const char *name, int length) const; + + void InitializeGlobalObject(const JSHandle &env, const JSHandle &globalObject); + + void InitializeFunction(const JSHandle &env, const JSHandle &emptyFuncDynclass) const; + + void InitializeObject(const JSHandle &env, const JSHandle &objFuncPrototype, + const JSHandle &objFunc); + + void InitializeNumber(const JSHandle &env, const JSHandle &globalObject, + const JSHandle &primRefObjDynclass); + + void InitializeDate(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeBoolean(const JSHandle &env, const JSHandle &primRefObjDynclass) const; + + void InitializeSymbol(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeSymbolWithRealm(const JSHandle &realm, const JSHandle &objFuncInstanceDynclass); + + void InitializeArray(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const; + + void InitializeTypedArray(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeInt8Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeUint8Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeUint8ClampedArray(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeInt16Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeUint16Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeInt32Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeUint32Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeFloat32Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeFloat64Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeAllTypeError(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeAllTypeErrorWithRealm(const JSHandle &realm) const; + + void InitializeError(const JSHandle &env, const JSHandle &objFuncDynclass, + const JSType &errorTag) const; + + void SetErrorWithRealm(const JSHandle &realm, const JSType &errorTag) const; + + void InitializeRegExp(const JSHandle &env); + + void GeneralUpdateError(ErrorParameter *error, EcmaEntrypoint constructor, EcmaEntrypoint method, const char *name, + JSType type) const; + + void InitializeSet(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeMap(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeWeakMap(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeWeakSet(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeMath(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const; + + void InitializeJson(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const; + + void InitializeString(const JSHandle &env, const JSHandle &primRefObjDynclass) const; + + void InitializeIterator(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeStringIterator(const JSHandle &env, const JSHandle &iteratorFuncDynclass) const; + + void InitializeForinIterator(const JSHandle &env, const JSHandle &iteratorFuncDynclass) const; + + void InitializeMapIterator(const JSHandle &env, const JSHandle &iteratorFuncDynclass) const; + + void InitializeSetIterator(const JSHandle &env, const JSHandle &iteratorFuncDynclass) const; + + void InitializeArrayIterator(const JSHandle &env, const JSHandle &iteratorFuncDynclass) const; + + void InitializeArrayBuffer(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeDataView(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeProxy(const JSHandle &env); + + void InitializeReflect(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const; + + void InitializeAsyncFunction(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeGeneratorFunction(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeGenerator(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + JSHandle InitializeExoticConstructor(const JSHandle &env, EcmaEntrypoint ctorFunc, + const char *name, int length); + + void InitializePromise(const JSHandle &env, const JSHandle &promiseFuncDynclass); + + void InitializePromiseJob(const JSHandle &env); + + void SetFunction(const JSHandle &env, const JSHandle &obj, const char *key, + EcmaEntrypoint func, int length) const; + + void SetFunction(const JSHandle &env, const JSHandle &obj, const JSHandle &key, + EcmaEntrypoint func, int length) const; + + void SetFuncToObjAndGlobal(const JSHandle &env, const JSHandle &globalObject, + const JSHandle &obj, const char *key, EcmaEntrypoint func, int length); + + template + void SetFunctionAtSymbol(const JSHandle &env, const JSHandle &obj, + const JSHandle &symbol, const char *name, EcmaEntrypoint func, + int length) const; + + void SetStringTagSymbol(const JSHandle &env, const JSHandle &obj, const char *key) const; + JSHandle CreateGetter(const JSHandle &env, EcmaEntrypoint func, const char *name, + int length) const; + + void SetConstant(const JSHandle &obj, const char *key, JSTaggedValue value) const; + + void SetGlobalThis(const JSHandle &obj, const char *key, const JSHandle &globalValue); + + void SetAttribute(const JSHandle &obj, const char *key, const char *value) const; + + void SetNoneAttributeProperty(const JSHandle &obj, const char *key, + const JSHandle &value) const; + + void StrictModeForbiddenAccessCallerArguments(const JSHandle &env, + const JSHandle &prototype) const; + + JSHandle CreateSetter(const JSHandle &env, EcmaEntrypoint func, const char *name, + int length); + void SetArgumentsSharedAccessor(const JSHandle &env); + void SetAccessor(const JSHandle &obj, const JSHandle &key, + const JSHandle &getter, const JSHandle &setter) const; + void SetGetter(const JSHandle &obj, const JSHandle &key, + const JSHandle &getter) const; + void InitializeJSNativeObject(const JSHandle &env) const; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMA_INIT_H diff --git a/ecmascript/builtins/builtins_array.cpp b/ecmascript/builtins/builtins_array.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3c89e2526ee89ce67d29804421d6010c8b66aa27 --- /dev/null +++ b/ecmascript/builtins/builtins_array.cpp @@ -0,0 +1,2637 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_array.h" + +#include + +#include "ecmascript/base/array_helper.h" +#include "ecmascript/base/number_helper.h" +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/base/typed_array_helper.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/global_env.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_stable_array.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript::builtins { +using ArrayHelper = base::ArrayHelper; +using TypedArrayHelper = base::TypedArrayHelper; + +// 22.1.1 +JSTaggedValue BuiltinsArray::ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let numberOfArgs be the number of arguments passed to this function call. + array_size_t argc = argv->GetArgsNumber(); + + // 3. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + JSHandle constructor = GetConstructor(argv); + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + newTarget = constructor; + } + + // 4. Let proto be GetPrototypeFromConstructor(newTarget, "%ArrayPrototype%"). + // In NewJSObjectByConstructor(), will get prototype. + // 5. ReturnIfAbrupt(proto). + + // 22.1.1.1 Array ( ) + if (argc == 0) { + // 6. Return ArrayCreate(0, proto). + return JSTaggedValue(JSArray::ArrayCreate(thread, JSTaggedNumber(0), newTarget).GetObject()); + } + + // 22.1.1.2 Array(len) + if (argc == 1) { + // 6. Let array be ArrayCreate(0, proto). + uint32_t newLen = 0; + JSHandle newArrayHandle(JSArray::ArrayCreate(thread, JSTaggedNumber(newLen), newTarget)); + JSHandle len = GetCallArg(argv, 0); + // 7. If Type(len) is not Number, then + // a. Let defineStatus be CreateDataProperty(array, "0", len). + // b. Assert: defineStatus is true. + // c. Let intLen be 1. + // 8. Else, + // a. Let intLen be ToUint32(len). + // b. If intLen ≠ len, throw a RangeError exception. + // 9. Let setStatus be Set(array, "length", intLen, true). + // 10. Assert: setStatus is not an abrupt completion. + if (!len->IsNumber()) { + JSHandle key0(factory->NewFromString("0")); + JSObject::CreateDataProperty(thread, newArrayHandle, key0, len); + newLen = 1; + } else { + newLen = JSTaggedValue::ToUint32(thread, len); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (JSTaggedNumber(len.GetTaggedValue()).GetNumber() != newLen) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The length is out of range.", JSTaggedValue::Exception()); + } + } + JSArray::Cast(*newArrayHandle)->SetArrayLength(thread, newLen); + + // 11. Return array. + return newArrayHandle.GetTaggedValue(); + } + + // 22.1.1.3 Array(...items ) + JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(argc), newTarget).GetTaggedValue(); + if (!newArray.IsArray(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create array.", JSTaggedValue::Exception()); + } + JSHandle newArrayHandle(thread, newArray); + + // 8. Let k be 0. + // 9. Let items be a zero-origined List containing the argument items in order. + // 10. Repeat, while k < numberOfArgs + // a. Let Pk be ToString(k). + // b. Let itemK be items[k]. + // c. Let defineStatus be CreateDataProperty(array, Pk, itemK). + // d. Assert: defineStatus is true. + // e. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + for (uint32_t k = 0; k < argc; k++) { + key.Update(JSTaggedValue(k)); + JSHandle itemK = GetCallArg(argv, k); + JSObject::CreateDataProperty(thread, newArrayHandle, key, itemK); + } + + // 11. Assert: the value of array’s length property is numberOfArgs. + // 12. Return array. + JSArray::Cast(*newArrayHandle)->SetArrayLength(thread, argc); + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.2.1 Array.from ( items [ , mapfn [ , thisArg ] ] ) +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsArray::From(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, From); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + // 1. Let C be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If mapfn is undefined, let mapping be false. + bool mapping = false; + // 3. else + // a. If IsCallable(mapfn) is false, throw a TypeError exception. + // b. If thisArg was supplied, let T be thisArg; else let T be undefined. + // c. Let mapping be true + JSHandle thisArgHandle = GetCallArg(argv, INDEX_TWO); + JSHandle mapfn = GetCallArg(argv, 1); + if (!mapfn->IsUndefined()) { + if (!mapfn->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the mapfn is not callable.", JSTaggedValue::Exception()); + } + mapping = true; + } + // 4. Let usingIterator be GetMethod(items, @@iterator). + JSHandle items = GetCallArg(argv, 0); + if (items->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The items is null.", JSTaggedValue::Exception()); + } + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle usingIterator = JSObject::GetMethod(thread, items, iteratorSymbol); + // 5. ReturnIfAbrupt(usingIterator). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + array_size_t length = 2; + JSHandle msg = factory->NewTaggedArray(length); + // 6. If usingIterator is not undefined, then + if (!usingIterator->IsUndefined()) { + // a. If IsConstructor(C) is true, then + // i. Let A be Construct(C). + // b. Else, + // i. Let A be ArrayCreate(0). + // c. ReturnIfAbrupt(A). + JSTaggedValue newArray; + if (thisHandle->IsConstructor()) { + newArray = JSFunction::Construct(thread, thisHandle, factory->EmptyArray(), + JSHandle(thread, JSTaggedValue::Undefined())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + if (!newArray.IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to construct the array.", JSTaggedValue::Exception()); + } + JSHandle newArrayHandle(thread, newArray); + // d. Let iterator be GetIterator(items, usingIterator). + JSHandle iterator = JSIterator::GetIterator(thread, items, usingIterator); + // e. ReturnIfAbrupt(iterator). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // f. Let k be 0. + int k = 0; + // g. Repeat + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + while (true) { + key.Update(JSTaggedValue(k)); + // i. Let Pk be ToString(k). + // ii. Let next be IteratorStep(iterator). + JSHandle next = JSIterator::IteratorStep(thread, iterator); + // iii. ReturnIfAbrupt(next). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // iv. If next is false, then + // 1. Let setStatus be Set(A, "length", k, true). + // 2. ReturnIfAbrupt(setStatus). + // 3. Return A. + if (next->IsFalse()) { + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, key, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(newArrayHandle.GetTaggedValue()); + } + // v. Let nextValue be IteratorValue(next). + JSHandle nextValue = JSIterator::IteratorValue(thread, next); + // vi. ReturnIfAbrupt(nextValue). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // vii. If mapping is true, then + // 1. Let mappedValue be Call(mapfn, T, «nextValue, k»). + // 2. If mappedValue is an abrupt completion, return IteratorClose(iterator, mappedValue). + // 3. Let mappedValue be mappedValue.[[value]]. + // viii. Else, let mappedValue be nextValue. + JSHandle mapValue; + if (mapping) { + msg->Set(thread, 0, nextValue); + msg->Set(thread, 1, JSTaggedValue(k)); + JSTaggedValue callResult = JSFunction::Call(thread, mapfn, thisArgHandle, msg); + mapValue = JSHandle(thread, callResult); + JSTaggedValue mapResult = JSIterator::IteratorClose(thread, iterator, mapValue).GetTaggedValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue(mapResult)); + } else { + mapValue = nextValue; + } + // ix. Let defineStatus be CreateDataPropertyOrThrow(A, Pk, mappedValue). + // x. If defineStatus is an abrupt completion, return IteratorClose(iterator, defineStatus). + // xi. Increase k by 1. + JSHandle defineStatus( + thread, JSTaggedValue(JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, key, mapValue))); + JSTaggedValue defineResult = JSIterator::IteratorClose(thread, iterator, defineStatus).GetTaggedValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue(defineResult)); + k++; + } + } + // 7. Assert: items is not an Iterable so assume it is an array-like object. + // 8. Let arrayLike be ToObject(items). + JSHandle arrayLikeObj = JSTaggedValue::ToObject(thread, items); + // 9. ReturnIfAbrupt(arrayLike). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle arrayLike(arrayLikeObj); + // 10. Let len be ToLength(Get(arrayLike, "length")). + double len = ArrayHelper::GetArrayLength(thread, arrayLike); + // 11. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 12. If IsConstructor(C) is true, then + // a. Let A be Construct(C, «len»). + // 13. Else, + // a. Let A be ArrayCreate(len). + // 14. ReturnIfAbrupt(A). + JSTaggedValue newArray; + if (thisHandle->IsConstructor()) { + array_size_t arrayLength = 1; + JSHandle array = factory->NewTaggedArray(arrayLength); + array->Set(thread, 0, JSTaggedValue(len)); + newArray = JSFunction::Construct(thread, thisHandle, array, + JSHandle(thread, JSTaggedValue::Undefined())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(len)).GetTaggedValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + if (!newArray.IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to construct the array.", JSTaggedValue::Exception()); + } + JSHandle newArrayHandle(thread, newArray); + // 15. Let k be 0. + // 16. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kValue be Get(arrayLike, Pk). + // d. If mapping is true, then + // i. Let mappedValue be Call(mapfn, T, «kValue, k»). + // e. Else, let mappedValue be kValue. + // f. Let defineStatus be CreateDataPropertyOrThrow(A, Pk, mappedValue). + JSHandle mapValue; + double k = 0; + while (k < len) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, arrayLike, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (mapping) { + msg->Set(thread, 0, kValue); + msg->Set(thread, 1, JSTaggedValue(k)); + JSTaggedValue callResult = JSFunction::Call(thread, mapfn, thisArgHandle, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + mapValue = JSHandle(thread, callResult); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + mapValue = kValue; + } + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // 17. Let setStatus be Set(A, "length", len, true). + JSHandle lenHandle(thread, JSTaggedValue(len)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, lenHandle, true); + // 18. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 19. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.2.2 Array.isArray ( arg ) +JSTaggedValue BuiltinsArray::IsArray(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, IsArray); + // 1. Return IsArray(arg). + if (GetCallArg(argv, 0)->IsArray(argv->GetThread())) { + return GetTaggedBoolean(true); + } + return GetTaggedBoolean(false); +} + +// 22.1.2.3 Array.of ( ...items ) +JSTaggedValue BuiltinsArray::Of(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Of); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle lengthKey = globalConst->GetHandledLengthString(); + + // 1. Let len be the actual number of arguments passed to this function. + array_size_t argc = argv->GetArgsNumber(); + + // 3. Let C be the this value. + JSHandle thisHandle = GetThis(argv); + // 4. If IsConstructor(C) is true, then + // a. Let A be Construct(C, «len»). + // 5. Else, + // a. Let A be ArrayCreate(len). + // 6. ReturnIfAbrupt(A). + JSHandle newArray; + if (thisHandle->IsConstructor()) { + JSHandle undefined = globalConst->GetHandledUndefined(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle arguments = factory->NewTaggedArray(1); + arguments->Set(thread, 0, JSTaggedValue(argc)); + JSTaggedValue taggedArray = JSFunction::Construct(thread, thisHandle, arguments, undefined); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + newArray = JSHandle(thread, taggedArray); + } else { + newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(argc)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + if (!newArray->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create Object.", JSTaggedValue::Exception()); + } + JSHandle newArrayHandle(newArray); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let kValue be items[k]. + // b. Let Pk be ToString(k). + // c. Let defineStatus be CreateDataPropertyOrThrow(A,Pk, kValue). + // d. ReturnIfAbrupt(defineStatus). + // e. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + for (uint32_t k = 0; k < argc; k++) { + key.Update(JSTaggedValue(k)); + JSHandle kValue = GetCallArg(argv, k); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, key, kValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 9. Let setStatus be Set(A, "length", len, true). + JSHandle lenHandle(thread, JSTaggedValue(argc)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, lenHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.2.5 get Array [ @@species ] +JSTaggedValue BuiltinsArray::Species([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // 1. Return the this value. + return GetThis(argv).GetTaggedValue(); +} + +// 22.1.3.1 Array.prototype.concat ( ...arguments ) +JSTaggedValue BuiltinsArray::Concat(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Concat); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + array_size_t argc = argv->GetArgsNumber(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let A be ArraySpeciesCreate(O, 0). + uint32_t arrayLen = 0; + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen)); + // 4. ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + + // 5. Let n be 0. + double n = 0; + JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + bool isSpreadable = ArrayHelper::IsConcatSpreadable(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (isSpreadable) { + double thisLen = ArrayHelper::GetArrayLength(thread, thisObjVal); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (n + thisLen > base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + double k = 0; + while (k < thisLen) { + fromKey.Update(JSTaggedValue(k)); + toKey.Update(JSTaggedValue(n)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + n++; + k++; + } + } else { + if (n >= base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, n, thisObjVal); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + n++; + } + // 7. Repeat, while items is not empty + for (uint32_t i = 0; i < argc; i++) { + // a. Remove the first element from items and let E be the value of the element + JSHandle addHandle = GetCallArg(argv, i); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle addObjHandle(addHandle); + + // b. Let spreadable be IsConcatSpreadable(E). + isSpreadable = ArrayHelper::IsConcatSpreadable(thread, addHandle); + // c. ReturnIfAbrupt(spreadable). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // d. If spreadable is true, then + if (isSpreadable) { + // ii. Let len be ToLength(Get(E, "length")). + double len = ArrayHelper::GetArrayLength(thread, JSHandle::Cast(addObjHandle)); + // iii. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // iv. If n + len > 253-1, throw a TypeError exception. + if (n + len > base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + double k = 0; + // v. Repeat, while k < len + while (k < len) { + fromKey.Update(JSTaggedValue(k)); + toKey.Update(JSTaggedValue(n)); + // 1. Let P be ToString(k). + // 2. Let exists be HasProperty(E, P). + // 4. If exists is true, then + bool exists = JSTaggedValue::HasProperty(thread, JSHandle::Cast(addObjHandle), fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + // a. Let subElement be Get(E, P). + JSHandle fromValHandle = JSArray::FastGetPropertyByValue(thread, addHandle, fromKey); + // b. ReturnIfAbrupt(subElement). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValHandle); + // d. ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 5. Increase n by 1. + // 6. Increase k by 1. + n++; + k++; + } + } else { // e. Else E is added as a single item rather than spread, + // i. If n≥253-1, throw a TypeError exception. + if (n >= base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + // ii. Let status be CreateDataPropertyOrThrow (A, ToString(n), E). + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, n, addHandle); + // iii. ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // iv. Increase n by 1. + n++; + } + } + // 8. Let setStatus be Set(A, "length", n, true). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle lenHandle(thread, JSTaggedValue(n)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, lenHandle, true); + // 9. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 10. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.3.3 Array.prototype.copyWithin (target, start [ , end ] ) +JSTaggedValue BuiltinsArray::CopyWithin(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, CopyWithin); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, GetThis(argv)); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + double copyTo; + double copyFrom; + double copyEnd; + + // 5. Let relativeTarget be ToInteger(target). + JSTaggedNumber targetTemp = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0)); + // 6. ReturnIfAbrupt(relativeTarget). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double target = targetTemp.GetNumber(); + // 7. If relativeTarget < 0, let to be max((len + relativeTarget),0); else let to be min(relativeTarget, len). + if (target < 0) { + copyTo = target + len > 0 ? target + len : 0; + } else { + copyTo = target < len ? target : len; + } + + // 8. Let relativeStart be ToInteger(start). + JSTaggedNumber start_t = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 1)); + // 9. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double start = start_t.GetNumber(); + // 10. If relativeStart < 0, let from be max((len + relativeStart),0); else let from be min(relativeStart, len). + if (start < 0) { + copyFrom = start + len > 0 ? start + len : 0; + } else { + copyFrom = start < len ? start : len; + } + + // 11. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end). + double end = len; + JSHandle msg3 = GetCallArg(argv, INDEX_TWO); + if (!msg3->IsUndefined()) { + JSTaggedNumber temp = JSTaggedValue::ToInteger(thread, msg3); + // 12. ReturnIfAbrupt(relativeEnd). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + end = temp.GetNumber(); + } + + // 13. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len). + if (end < 0) { + copyEnd = end + len > 0 ? end + len : 0; + } else { + copyEnd = end < len ? end : len; + } + + // 14. Let count be min(final-from, len-to). + double count = (copyEnd - copyFrom < len - copyTo) ? (copyEnd - copyFrom) : (len - copyTo); + + // 15. If from 0 + // a. Let fromKey be ToString(from). + // b. Let toKey be ToString(to). + // c. Let fromPresent be HasProperty(O, fromKey). + // d. ReturnIfAbrupt(fromPresent). + // e. If fromPresent is true, then + // i. Let fromVal be Get(O, fromKey). + // ii. ReturnIfAbrupt(fromVal). + // iii. Let setStatus be Set(O, toKey, fromVal, true). + // iv. ReturnIfAbrupt(setStatus). + // f. Else fromPresent is false, + // i. Let deleteStatus be DeletePropertyOrThrow(O, toKey). + // ii. ReturnIfAbrupt(deleteStatus). + // g. Let from be from + direction. + // h. Let to be to + direction. + // i. Let count be count − 1. + JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + while (count > 0) { + fromKey.Update(JSTaggedValue(copyFrom)); + toKey.Update(JSTaggedValue(copyTo)); + bool exists = (thisObjVal->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, fromKey)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + if (thisObjVal->IsJSProxy()) { + toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue()); + } + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + copyFrom = copyFrom + direction; + copyTo = copyTo + direction; + count--; + } + + // 18. Return O. + return thisObjHandle.GetTaggedValue(); +} + +// 22.1.3.4 Array.prototype.entries ( ) +JSTaggedValue BuiltinsArray::Entries(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Entries); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let O be ToObject(this value). + // 2. ReturnIfAbrupt(O). + JSHandle self = JSTaggedValue::ToObject(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return CreateArrayIterator(O, "key+value"). + JSHandle iter(factory->NewJSArrayIterator(self, IterationKind::KEY_AND_VALUE)); + return iter.GetTaggedValue(); +} + +// 22.1.3.5 Array.prototype.every ( callbackfn [ , thisArg] ) +JSTaggedValue BuiltinsArray::Every(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let testResult be ToBoolean(Call(callbackfn, T, «kValue, k, O»)). + // iv. ReturnIfAbrupt(testResult). + // v. If testResult is false, return false. + // e. Increase k by 1. + array_size_t length = 3; + JSHandle msg = factory->NewTaggedArray(length); + uint32_t k = 0; + while (k < len) { + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, 0, kValue); + msg->Set(thread, 1, JSTaggedValue(k)); + msg->Set(thread, INDEX_TWO, thisObjVal); + JSTaggedValue callResult = JSFunction::Call(thread, callbackFnHandle, thisArgHandle, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool boolResult = callResult.ToBoolean(); + if (!boolResult) { + return GetTaggedBoolean(false); + } + } + k++; + } + + // 9. Return true. + return GetTaggedBoolean(true); +} + +// 22.1.3.6 Array.prototype.fill (value [ , start [ , end ] ] ) +JSTaggedValue BuiltinsArray::Fill(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Fill); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + JSHandle value = GetCallArg(argv, 0); + if (thisHandle->IsTypedArray()) { + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + value = JSHandle(thread, JSTaggedValue(number.GetNumber())); + } + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let relativeStart be ToInteger(start). + double start; + JSHandle msg1 = GetCallArg(argv, 1); + JSTaggedNumber argStartTemp = JSTaggedValue::ToInteger(thread, msg1); + // 6. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double argStart = argStartTemp.GetNumber(); + // 7. If relativeStart < 0, let k be max((len + relativeStart),0); else let k be min(relativeStart, len). + if (argStart < 0) { + start = argStart + len > 0 ? argStart + len : 0; + } else { + start = argStart < len ? argStart : len; + } + + // 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end). + double argEnd = len; + JSHandle msg2 = GetCallArg(argv, INDEX_TWO); + if (!msg2->IsUndefined()) { + JSTaggedNumber argEndTemp = JSTaggedValue::ToInteger(thread, msg2); + // 9. ReturnIfAbrupt(relativeEnd). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + argEnd = argEndTemp.GetNumber(); + } + + // 10. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len). + double end; + if (argEnd < 0) { + end = argEnd + len > 0 ? argEnd + len : 0; + } else { + end = argEnd < len ? argEnd : len; + } + + // 11. Repeat, while k < final + // a. Let Pk be ToString(k). + // b. Let setStatus be Set(O, Pk, value, true). + // c. ReturnIfAbrupt(setStatus). + // d. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + double k = start; + while (k < end) { + key.Update(JSTaggedValue(k)); + JSArray::FastSetPropertyByValue(thread, thisObjVal, key, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + + // 12. Return O. + return thisObjHandle.GetTaggedValue(); +} + +// 22.1.3.7 Array.prototype.filter ( callbackfn [ , thisArg ] ) +JSTaggedValue BuiltinsArray::Filter(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let A be ArraySpeciesCreate(O, 0). + int32_t arrayLen = 0; + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen)); + // 8. ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + + // 9. Let k be 0. + // 10. Let to be 0. + // 11. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let selected be ToBoolean(Call(callbackfn, T, «kValue, k, O»)). + // iv. ReturnIfAbrupt(selected). + // v. If selected is true, then + // 1. Let status be CreateDataPropertyOrThrow (A, ToString(to), kValue). + // 2. ReturnIfAbrupt(status). + // 3. Increase to by 1. + // e. Increase k by 1. + double toIndex = 0; + array_size_t length = 3; + JSHandle msg = factory->NewTaggedArray(length); + + JSMutableHandle toIndexHandle(thread, JSTaggedValue::Undefined()); + uint32_t k = 0; + while (k < len) { + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, 0, kValueHandle); + msg->Set(thread, 1, JSTaggedValue(k)); + msg->Set(thread, INDEX_TWO, thisObjVal); + JSTaggedValue callResult = JSFunction::Call(thread, callbackFnHandle, thisArgHandle, msg); + bool boolResult = callResult.ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (boolResult) { + toIndexHandle.Update(JSTaggedValue(toIndex)); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + toIndex++; + } + } + k++; + } + + // 12. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.3.8 Array.prototype.find ( predicate [ , thisArg ] ) +JSTaggedValue BuiltinsArray::Find(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Find); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(predicate) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kValue be Get(O, Pk). + // c. ReturnIfAbrupt(kValue). + // d. Let testResult be ToBoolean(Call(predicate, T, «kValue, k, O»)). + // e. ReturnIfAbrupt(testResult). + // f. If testResult is true, return kValue. + // g. Increase k by 1. + array_size_t length = 3; + JSHandle msg = factory->NewTaggedArray(length); + uint32_t k = 0; + while (k < len) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, 0, kValue); + msg->Set(thread, 1, JSTaggedValue(k)); + msg->Set(thread, INDEX_TWO, thisObjVal); + JSTaggedValue callResult = JSFunction::Call(thread, callbackFnHandle, thisArgHandle, msg); + bool boolResult = callResult.ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (boolResult) { + return kValue.GetTaggedValue(); + } + k++; + } + + // 9. Return undefined. + return JSTaggedValue::Undefined(); +} + +// 22.1.3.9 Array.prototype.findIndex ( predicate [ , thisArg ] ) +JSTaggedValue BuiltinsArray::FindIndex(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, FindIndex); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(predicate) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kValue be Get(O, Pk). + // c. ReturnIfAbrupt(kValue). + // d. Let testResult be ToBoolean(Call(predicate, T, «kValue, k, O»)). + // e. ReturnIfAbrupt(testResult). + // f. If testResult is true, return k. + // g. Increase k by 1. + array_size_t length = 3; + JSHandle msg = factory->NewTaggedArray(length); + uint32_t k = 0; + while (k < len) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, 0, kValue); + msg->Set(thread, 1, JSTaggedValue(k)); + msg->Set(thread, INDEX_TWO, thisObjVal); + JSTaggedValue callResult = JSFunction::Call(thread, callbackFnHandle, thisArgHandle, msg); + bool boolResult = callResult.ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (boolResult) { + return GetTaggedDouble(k); + } + k++; + } + + // 9. Return -1. + return GetTaggedDouble(-1); +} + +// 22.1.3.10 Array.prototype.forEach ( callbackfn [ , thisArg ] ) +JSTaggedValue BuiltinsArray::ForEach(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»). + // iv. ReturnIfAbrupt(funcResult). + // e. Increase k by 1. + array_size_t length = 3; + JSHandle msg = factory->NewTaggedArray(length); + uint32_t k = 0; + while (k < len) { + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, 0, kValue); + msg->Set(thread, 1, JSTaggedValue(k)); + msg->Set(thread, INDEX_TWO, thisObjVal); + JSTaggedValue funcResult = JSFunction::Call(thread, callbackFnHandle, thisArgHandle, msg); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); + } + k++; + } + + // 9. Return undefined. + return JSTaggedValue::Undefined(); +} + +// 22.1.3.11 Array.prototype.indexOf ( searchElement [ , fromIndex ] ) +JSTaggedValue BuiltinsArray::IndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, IndexOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + array_size_t argc = argv->GetArgsNumber(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + JSHandle searchElement = GetCallArg(argv, 0); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If len is 0, return −1. + if (len == 0) { + return GetTaggedInt(-1); + } + + // 6. If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be 0. + double fromIndex = 0; + if (argc > 1) { + JSHandle msg1 = GetCallArg(argv, 1); + JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, msg1); + // 7. ReturnIfAbrupt(n). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + fromIndex = base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber()); + } + + // 8. If n ≥ len, return −1. + if (fromIndex >= len) { + return GetTaggedInt(-1); + } + + // 9. If n ≥ 0, then + // a. Let k be n. + // 10. Else n<0, + // a. Let k be len - abs(n). + // b. If k < 0, let k be 0. + double from = (fromIndex >= 0) ? fromIndex : ((len + fromIndex) >= 0 ? len + fromIndex : 0); + + // 11. Repeat, while k key(thread, JSTaggedValue::Undefined()); + while (from < len) { + key.Update(JSTaggedValue(from)); + bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, key)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (JSTaggedValue::StrictEqual(thread, searchElement, kValueHandle)) { + return GetTaggedDouble(from); + } + } + from++; + } + + // 12. Return -1. + return GetTaggedInt(-1); +} + +// 22.1.3.12 Array.prototype.join (separator) +JSTaggedValue BuiltinsArray::Join(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Join); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisHandle = GetThis(argv); + if (thisHandle->IsStableJSArray(thread)) { + return JSStableArray::Join(JSHandle::Cast(thisHandle), argv); + } + auto factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If separator is undefined, let separator be the single-element String ",". + // 6. Let sep be ToString(separator). + JSHandle sepHandle; + if ((GetCallArg(argv, 0)->IsUndefined())) { + sepHandle = JSHandle::Cast(factory->NewFromString(",")); + } else { + sepHandle = GetCallArg(argv, 0); + } + + JSHandle sepStringHandle = JSTaggedValue::ToString(thread, sepHandle); + // 7. ReturnIfAbrupt(sep). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t sepLen = sepStringHandle->GetLength(); + std::u16string sepStr; + if (sepStringHandle->IsUtf16()) { + sepStr = base::StringHelper::Utf16ToU16String(sepStringHandle->GetDataUtf16(), sepLen); + } else { + sepStr = base::StringHelper::Utf8ToU16String(sepStringHandle->GetDataUtf8(), sepLen); + } + + // 8. If len is zero, return the empty String. + if (len == 0) { + return GetTaggedString(thread, ""); + } + + // 9. Let element0 be Get(O, "0"). + // 10. If element0 is undefined or null, let R be the empty String; otherwise, let R be ToString(element0). + // 11. ReturnIfAbrupt(R). + // 12. Let k be 1. + // 13. Repeat, while k < len + // a. Let S be the String value produced by concatenating R and sep. + // b. Let element be Get(O, ToString(k)). + // c. If element is undefined or null, let next be the empty String; otherwise, let next be ToString(element). + // d. ReturnIfAbrupt(next). + // e. Let R be a String value produced by concatenating S and next. + // f. Increase k by 1. + std::u16string concatStr; + std::u16string concatStrNew; + for (int32_t k = 0; k < len; k++) { + std::u16string nextStr; + JSHandle element = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!element->IsUndefined() && !element->IsNull()) { + JSHandle nextStringHandle = JSTaggedValue::ToString(thread, element); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t nextLen = nextStringHandle->GetLength(); + if (nextStringHandle->IsUtf16()) { + nextStr = base::StringHelper::Utf16ToU16String(nextStringHandle->GetDataUtf16(), nextLen); + } else { + nextStr = base::StringHelper::Utf8ToU16String(nextStringHandle->GetDataUtf8(), nextLen); + } + } + if (k > 0) { + concatStrNew = base::StringHelper::Append(concatStr, sepStr); + concatStr = base::StringHelper::Append(concatStrNew, nextStr); + continue; + } + concatStr = base::StringHelper::Append(concatStr, nextStr); + } + + // 14. Return R. + const char16_t *constChar16tData = concatStr.data(); + auto *char16tData = const_cast(constChar16tData); + auto *uint16tData = reinterpret_cast(char16tData); + int32_t u16strSize = concatStr.size(); + return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue(); +} + +// 22.1.3.13 Array.prototype.keys ( ) +JSTaggedValue BuiltinsArray::Keys(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Keys); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let O be ToObject(this value). + // 2. ReturnIfAbrupt(O). + JSHandle self = JSTaggedValue::ToObject(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return CreateArrayIterator(O, "key"). + JSHandle iter(factory->NewJSArrayIterator(self, IterationKind::KEY)); + return iter.GetTaggedValue(); +} + +// 22.1.3.14 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] ) +JSTaggedValue BuiltinsArray::LastIndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, LastIndexOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + array_size_t argc = argv->GetArgsNumber(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + JSHandle searchElement = GetCallArg(argv, 0); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If len is 0, return −1. + if (len == 0) { + return GetTaggedInt(-1); + } + + // 6. If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be len-1. + double fromIndex = len - 1; + if (argc > 1) { + JSHandle msg1 = GetCallArg(argv, 1); + JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, msg1); + // 7. ReturnIfAbrupt(n). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + fromIndex = base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber()); + } + + // 8. If n ≥ 0, let k be min(n, len – 1). + // 9. Else n < 0, + // a. Let k be len - abs(n). + double from = (fromIndex >= 0) ? ((len - 1) < fromIndex ? len - 1 : fromIndex) : len + fromIndex; + + // 10. Repeat, while k≥ 0 + // a. Let kPresent be HasProperty(O, ToString(k)). + // b. ReturnIfAbrupt(kPresent). + // c. If kPresent is true, then + // i. Let elementK be Get(O, ToString(k)). + // ii. ReturnIfAbrupt(elementK). + // iii. Let same be the result of performing Strict Equality Comparison searchElement === elementK. + // iv. If same is true, return k. + // d. Decrease k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + while (from >= 0) { + key.Update(JSTaggedValue(from)); + bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, key)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (JSTaggedValue::StrictEqual(thread, searchElement, kValueHandle)) { + return GetTaggedDouble(from); + } + } + from--; + } + + // 11. Return -1. + return GetTaggedInt(-1); +} + +// 22.1.3.15 Array.prototype.map ( callbackfn [ , thisArg ] ) +JSTaggedValue BuiltinsArray::Map(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Map); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let A be ArraySpeciesCreate(O, len). + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(len)); + // 8. ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!newArray.IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create Object.", JSTaggedValue::Exception()); + } + JSHandle newArrayHandle(thread, newArray); + + // 9. Let k be 0. + // 10. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let mappedValue be Call(callbackfn, T, «kValue, k, O»). + // iv. ReturnIfAbrupt(mappedValue). + // v. Let status be CreateDataPropertyOrThrow (A, Pk, mappedValue). + // vi. ReturnIfAbrupt(status). + // e. Increase k by 1. + array_size_t length = 3; + JSHandle msg = factory->NewTaggedArray(length); + uint32_t k = 0; + while (k < len) { + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, 0, kValue); + msg->Set(thread, 1, JSTaggedValue(k)); + msg->Set(thread, INDEX_TWO, thisObjVal); + JSTaggedValue mapResult = JSFunction::Call(thread, callbackFnHandle, thisArgHandle, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle kValueHandle(thread, mapResult); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, kValueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + } + + // 11. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.3.16 Array.prototype.pop ( ) +JSTaggedValue BuiltinsArray::Pop(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Pop); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + if (thisHandle->IsStableJSArray(thread)) { + return JSStableArray::Pop(JSHandle::Cast(thisHandle), argv); + } + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. If len is zero, + // a. Let setStatus be Set(O, "length", 0, true). + // b. ReturnIfAbrupt(setStatus). + // c. Return undefined. + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + if (len == 0) { + JSHandle lengthValue(thread, JSTaggedValue(0)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, lengthValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::Undefined(); + } + + // 6. Else len > 0, + // a. Let newLen be len–1. + // b. Let indx be ToString(newLen). + // c. Let element be Get(O, indx). + // d. ReturnIfAbrupt(element). + // e. Let deleteStatus be DeletePropertyOrThrow(O, indx). + // f. ReturnIfAbrupt(deleteStatus). + // g. Let setStatus be Set(O, "length", newLen, true). + // h. ReturnIfAbrupt(setStatus). + // i. Return element. + double newLen = len - 1; + JSHandle indexHandle(thread, JSTaggedValue(newLen)); + JSHandle element = JSTaggedValue::GetProperty(thread, thisObjVal, indexHandle).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, indexHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, indexHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return element.GetTaggedValue(); +} + +// 22.1.3.17 Array.prototype.push ( ...items ) +JSTaggedValue BuiltinsArray::Push(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Push); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisHandle = GetThis(argv); + if (thisHandle->IsStableJSArray(thread)) { + return JSStableArray::Push(JSHandle::Cast(thisHandle), argv); + } + // 6. Let argCount be the number of elements in items. + array_size_t argc = argv->GetArgsNumber(); + + // 1. Let O be ToObject(this value). + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. If len + argCount > 253-1, throw a TypeError exception. + if (len + argc > base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + + // 8. Repeat, while items is not empty + // a. Remove the first element from items and let E be the value of the element. + // b. Let setStatus be Set(O, ToString(len), E, true). + // c. ReturnIfAbrupt(setStatus). + // d. Let len be len+1. + double k = 0; + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + while (k < argc) { + key.Update(JSTaggedValue(len)); + JSHandle kValue = GetCallArg(argv, k); + JSArray::FastSetPropertyByValue(thread, thisObjVal, key, kValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + len++; + } + + // 9. Let setStatus be Set(O, "length", len, true). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + key.Update(JSTaggedValue(len)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, key, true); + // 10. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 11. Return len. + return GetTaggedDouble(len); +} + +// 22.1.3.18 Array.prototype.reduce ( callbackfn [ , initialValue ] ) +JSTaggedValue BuiltinsArray::Reduce(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Reduce); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + array_size_t argc = argv->GetArgsNumber(); + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If len is 0 and initialValue is not present, throw a TypeError exception. + if (len == 0 && argc < 2) { // 2:2 means the number of parameters + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + + // 7. Let k be 0. + // 8. If initialValue is present, then + // a. Set accumulator to initialValue. + // 9. Else initialValue is not present, + // a. Let kPresent be false. + // b. Repeat, while kPresent is false and k < len + // i. Let Pk be ToString(k). + // ii. Let kPresent be HasProperty(O, Pk). + // iii. ReturnIfAbrupt(kPresent). + // iv. If kPresent is true, then + // 1. Let accumulator be Get(O, Pk). + // 2. ReturnIfAbrupt(accumulator). + // v. Increase k by 1. + // c. If kPresent is false, throw a TypeError exception. + uint32_t k = 0; + JSMutableHandle accumulator(thread, JSTaggedValue::Undefined()); + if (argc == 2) { // 2:2 means the number of parameters + accumulator.Update(GetCallArg(argv, 1).GetTaggedValue()); + } else { + bool kPresent = false; + while (!kPresent && k < len) { + kPresent = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (kPresent) { + accumulator.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + } + if (!kPresent) { + THROW_TYPE_ERROR_AND_RETURN(thread, "accumulator can't be initialized.", JSTaggedValue::Exception()); + } + } + + // 10. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let accumulator be Call(callbackfn, undefined, «accumulator, kValue, k, O»). + // iv. ReturnIfAbrupt(accumulator). + // e. Increase k by 1. + JSTaggedValue callResult = JSTaggedValue::Undefined(); + array_size_t length = 4; + JSHandle msg = factory->NewTaggedArray(length); + while (k < len) { + bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, 0, accumulator.GetTaggedValue()); + msg->Set(thread, 1, kValue); + msg->Set(thread, INDEX_TWO, JSTaggedValue(k)); + msg->Set(thread, INDEX_THREE, thisObjVal); + JSHandle thisArgHandle = globalConst->GetHandledUndefined(); + callResult = JSFunction::Call(thread, callbackFnHandle, thisArgHandle, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + accumulator.Update(callResult); + } + k++; + } + + // 11. Return accumulator. + return accumulator.GetTaggedValue(); +} + +// 22.1.3.19 Array.prototype.reduceRight ( callbackfn [ , initialValue ] ) +JSTaggedValue BuiltinsArray::ReduceRight(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, ReduceRight); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + array_size_t argc = argv->GetArgsNumber(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If len is 0 and initialValue is not present, throw a TypeError exception. + if (len == 0 && argc < 2) { // 2:2 means the number of parameters + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + + // 7. Let k be len-1. + double k = len - 1; + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + // 8. If initialValue is present, then + // a. Set accumulator to initialValue. + // 9. Else initialValue is not present, + // a. Let kPresent be false. + // b. Repeat, while kPresent is false and k ≥ 0 + // i. Let Pk be ToString(k). + // ii. Let kPresent be HasProperty(O, Pk). + // iii. ReturnIfAbrupt(kPresent). + // iv. If kPresent is true, then + // 1. Let accumulator be Get(O, Pk). + // 2. ReturnIfAbrupt(accumulator). + // v. Decrease k by 1. + // c. If kPresent is false, throw a TypeError exception. + JSMutableHandle accumulator(thread, JSTaggedValue::Undefined()); + if (argc == 2) { // 2:2 means the number of parameters + accumulator.Update(GetCallArg(argv, 1).GetTaggedValue()); + } else { + bool kPresent = false; + while (!kPresent && k >= 0) { + key.Update(JSTaggedValue(k)); + kPresent = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, key)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (kPresent) { + accumulator.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, key).GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k--; + } + if (!kPresent) { + THROW_TYPE_ERROR_AND_RETURN(thread, "accumulator can't be initialized.", JSTaggedValue::Exception()); + } + } + + // 10. Repeat, while k ≥ 0 + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let accumulator be Call(callbackfn, undefined, «accumulator, kValue, k, O»). + // iv. ReturnIfAbrupt(accumulator). + // e. Decrease k by 1. + JSTaggedValue callResult = JSTaggedValue::Undefined(); + array_size_t length = 4; + JSHandle msg = factory->NewTaggedArray(length); + while (k >= 0) { + key.Update(JSTaggedValue(k)); + bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, key)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, 0, accumulator.GetTaggedValue()); + msg->Set(thread, 1, kValue); + msg->Set(thread, INDEX_TWO, JSTaggedValue(k)); + msg->Set(thread, INDEX_THREE, thisObjVal); + JSHandle thisArgHandle = globalConst->GetHandledUndefined(); + callResult = JSFunction::Call(thread, callbackFnHandle, thisArgHandle, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + accumulator.Update(callResult); + } + k--; + } + + // 11. Return accumulator. + return accumulator.GetTaggedValue(); +} + +// 22.1.3.20 Array.prototype.reverse ( ) +JSTaggedValue BuiltinsArray::Reverse(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Reverse); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let middle be floor(len/2). + double middle = std::floor(len / 2); + + // 6. Let lower be 0. + double lower = 0; + + // 7. Repeat, while lower != middle + // a. Let upper be len-lower-1. + // b. Let upperP be ToString(upper). + // c. Let lowerP be ToString(lower). + // d. Let lowerExists be HasProperty(O, lowerP). + // e. ReturnIfAbrupt(lowerExists). + // f. If lowerExists is true, then + // i. Let lowerValue be Get(O, lowerP). + // ii. ReturnIfAbrupt(lowerValue). + // g. Let upperExists be HasProperty(O, upperP). + // h. ReturnIfAbrupt(upperExists). + // i. If upperExists is true, then + // i. Let upperValue be Get(O, upperP). + // ii. ReturnIfAbrupt(upperValue). + // j. If lowerExists is true and upperExists is true, then + // i. Let setStatus be Set(O, lowerP, upperValue, true). + // ii. ReturnIfAbrupt(setStatus). + // iii. Let setStatus be Set(O, upperP, lowerValue, true). + // iv. ReturnIfAbrupt(setStatus). + // k. Else if lowerExists is false and upperExists is true, then + // i. Let setStatus be Set(O, lowerP, upperValue, true). + // ii. ReturnIfAbrupt(setStatus). + // iii. Let deleteStatus be DeletePropertyOrThrow (O, upperP). + // iv. ReturnIfAbrupt(deleteStatus). + // l. Else if lowerExists is true and upperExists is false, then + // i. Let deleteStatus be DeletePropertyOrThrow (O, lowerP). + // ii. ReturnIfAbrupt(deleteStatus). + // iii. Let setStatus be Set(O, upperP, lowerValue, true). + // iv. ReturnIfAbrupt(setStatus). + // m. Else both lowerExists and upperExists are false, + // i. No action is required. + // n. Increase lower by 1. + JSMutableHandle lowerP(thread, JSTaggedValue::Undefined()); + JSMutableHandle upperP(thread, JSTaggedValue::Undefined()); + JSHandle lowerValueHandle(thread, JSTaggedValue::Undefined()); + JSHandle upperValueHandle(thread, JSTaggedValue::Undefined()); + while (lower != middle) { + double upper = len - lower - 1; + lowerP.Update(JSTaggedValue(lower)); + upperP.Update(JSTaggedValue(upper)); + bool lowerExists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, lowerP)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (lowerExists) { + lowerValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, lowerP); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + bool upperExists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, upperP)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (upperExists) { + upperValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, upperP); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + if (lowerExists && upperExists) { + JSArray::FastSetPropertyByValue(thread, thisObjVal, lowerP, upperValueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, upperP, lowerValueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else if (upperExists) { + JSArray::FastSetPropertyByValue(thread, thisObjVal, lowerP, upperValueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, upperP); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else if (lowerExists) { + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, lowerP); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, upperP, lowerValueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + } + lower++; + } + + // 8. Return O . + return thisObjHandle.GetTaggedValue(); +} + +// 22.1.3.21 Array.prototype.shift ( ) +JSTaggedValue BuiltinsArray::Shift(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Shift); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. If len is zero, then + // a. Let setStatus be Set(O, "length", 0, true). + // b. ReturnIfAbrupt(setStatus). + // c. Return undefined. + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + if (len == 0) { + JSHandle zeroLenHandle(thread, JSTaggedValue(len)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, zeroLenHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::Undefined(); + } + + // 6. Let first be Get(O, "0"). + JSHandle firstKey(thread, JSTaggedValue(0)); + JSHandle firstValue = JSTaggedValue::GetProperty(thread, thisObjVal, firstKey).GetValue(); + // 7. ReturnIfAbrupt(first). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 8. Let k be 1. + // 9. Repeat, while k < len + // a. Let from be ToString(k). + // b. Let to be ToString(k–1). + // c. Let fromPresent be HasProperty(O, from). + // d. ReturnIfAbrupt(fromPresent). + // e. If fromPresent is true, then + // i. Let fromVal be Get(O, from). + // ii. ReturnIfAbrupt(fromVal). + // iii. Let setStatus be Set(O, to, fromVal, true). + // iv. ReturnIfAbrupt(setStatus). + // f. Else fromPresent is false, + // i. Let deleteStatus be DeletePropertyOrThrow(O, to). + // ii. ReturnIfAbrupt(deleteStatus). + // g. Increase k by 1. + JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + double k = 1; + while (k < len) { + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, k - 1, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + toKey.Update(JSTaggedValue(k - 1)); + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + } + // 10. Let deleteStatus be DeletePropertyOrThrow(O, ToString(len–1)). + JSHandle deleteKey(thread, JSTaggedValue(len - 1)); + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, deleteKey); + // 11. ReturnIfAbrupt(deleteStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 12. Let setStatus be Set(O, "length", len–1, true). + JSHandle newLenHandle(thread, JSTaggedValue(len - 1)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true); + // 13. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 14. Return first. + return firstValue.GetTaggedValue(); +} + +// 22.1.3.22 Array.prototype.slice (start, end) +JSTaggedValue BuiltinsArray::Slice(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Array, Slice); + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let relativeStart be ToInteger(start). + JSHandle msg0 = GetCallArg(argv, 0); + JSTaggedNumber argStartTemp = JSTaggedValue::ToInteger(thread, msg0); + // 6. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double argStart = argStartTemp.GetNumber(); + + double k; + // 7. If relativeStart < 0, let k be max((len + relativeStart),0); else let k be min(relativeStart, len). + if (argStart < 0) { + k = argStart + len > 0 ? argStart + len : 0; + } else { + k = argStart < len ? argStart : len; + } + // 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end). + // 9. ReturnIfAbrupt(relativeEnd). + // 10. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len). + JSHandle msg1 = GetCallArg(argv, 1); + double argEnd = len; + if (!msg1->IsUndefined()) { + JSTaggedNumber argEndTemp = JSTaggedValue::ToInteger(thread, msg1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + argEnd = argEndTemp.GetNumber(); + } + double final; + if (argEnd < 0) { + final = argEnd + len > 0 ? argEnd + len : 0; + } else { + final = argEnd < len ? argEnd : len; + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 11. Let count be max(final – k, 0). + double count = (final - k) > 0 ? (final - k) : 0; + + // 12. Let A be ArraySpeciesCreate(O, count). + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(count)); + // 13. ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + + // 14. Let n be 0. + // 15. Repeat, while k < final + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let status be CreateDataPropertyOrThrow(A, ToString(n), kValue ). + // iv. ReturnIfAbrupt(status). + // e. Increase k by 1. + // f. Increase n by 1. + double n = 0; + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle nKey(thread, JSTaggedValue::Undefined()); + while (k < final) { + key.Update(JSTaggedValue(k)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + nKey.Update(JSTaggedValue(n)); + JSHandle kValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, nKey, kValueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + n++; + } + + // 16. Let setStatus be Set(A, "length", n, true). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle newLenHandle(thread, JSTaggedValue(n)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, newLenHandle, true); + // 17. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 18. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.3.23 Array.prototype.some ( callbackfn [ , thisArg ] ) +JSTaggedValue BuiltinsArray::Some(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Some); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let testResult be ToBoolean(Call(callbackfn, T, «kValue, k, and O»)). + // iv. ReturnIfAbrupt(testResult). + // v. If testResult is true, return true. + // e. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + array_size_t length = 3; + JSHandle msg = factory->NewTaggedArray(length); + double k = 0; + while (k < len) { + bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + key.Update(JSTaggedValue(k)); + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, 0, kValue); + msg->Set(thread, 1, JSTaggedValue(k)); + msg->Set(thread, INDEX_TWO, thisObjVal); + JSTaggedValue callResult = JSFunction::Call(thread, callbackFnHandle, thisArgHandle, msg); + bool boolResult = callResult.ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (boolResult) { + return GetTaggedBoolean(true); + } + } + k++; + } + + // 9. Return false. + return GetTaggedBoolean(false); +} + +// 22.1.3.24 Array.prototype.sort (comparefn) +JSTaggedValue BuiltinsArray::Sort(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Sort); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let obj be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception()); + } + + // 2. Let len be ToLength(Get(obj, "length")). + double len = ArrayHelper::GetArrayLength(thread, JSHandle(thisObjHandle)); + // 3. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + array_size_t length = 2; + JSHandle msg = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length); + JSMutableHandle presentValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle middleValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle previousValue(thread, JSTaggedValue::Undefined()); + for (int i = 1; i < len; i++) { + int beginIndex = 0; + int endIndex = i; + presentValue.Update(FastRuntimeStub::FastGetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), i)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + while (beginIndex < endIndex) { + int middleIndex = (beginIndex + endIndex) / 2; // 2 : half + middleValue.Update( + FastRuntimeStub::FastGetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), middleIndex)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t compareResult = ArrayHelper::SortCompare(thread, callbackFnHandle, middleValue, presentValue, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (compareResult > 0) { + endIndex = middleIndex; + } else { + beginIndex = middleIndex + 1; + } + } + + if (endIndex >= 0 && endIndex < i) { + for (int j = i; j > endIndex; j--) { + previousValue.Update( + FastRuntimeStub::FastGetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), j - 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + FastRuntimeStub::FastSetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), j, + previousValue.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + FastRuntimeStub::FastSetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), endIndex, + presentValue.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + + return thisObjHandle.GetTaggedValue(); +} + +// 22.1.3.25 Array.prototype.splice (start, deleteCount , ...items ) +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsArray::Splice(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Splice); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + array_size_t argc = argv->GetArgsNumber(); + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. Let relativeStart be ToInteger(start). + double start = 0; + double insertCount = 0; + double actualDeleteCount = 0; + double end = len; + double argStart = 0; + if (argc > 0) { + JSHandle msg0 = GetCallArg(argv, 0); + JSTaggedNumber argStartTemp = JSTaggedValue::ToInteger(thread, msg0); + // 6. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + argStart = argStartTemp.GetNumber(); + // 7. If relativeStart < 0, let actualStart be max((len + relativeStart),0); else let actualStart be + // min(relativeStart, len). + if (argStart < 0) { + start = argStart + len > 0 ? argStart + len : 0; + } else { + start = argStart < end ? argStart : end; + } + actualDeleteCount = len - start; + } + // 8. If the number of actual arguments is 0, then + // a. Let insertCount be 0. + // b. Let actualDeleteCount be 0. + // 9. Else if the number of actual arguments is 1, then + // a. Let insertCount be 0. + // b. Let actualDeleteCount be len – actualStart. + // 10. Else, + // a. Let insertCount be the number of actual arguments minus 2. + // b. Let dc be ToInteger(deleteCount). + // c. ReturnIfAbrupt(dc). + // d. Let actualDeleteCount be min(max(dc,0), len – actualStart). + if (argc > 1) { + insertCount = argc - 2; // 2:2 means there are two arguments before the insert items. + JSHandle msg1 = GetCallArg(argv, 1); + JSTaggedNumber dcTemp = JSTaggedValue::ToInteger(thread, msg1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double dc = dcTemp.GetNumber(); + actualDeleteCount = ((dc > 0 ? dc : 0) < len - start) ? (dc > 0 ? dc : 0) : len - start; + } + // 11. If len+insertCount−actualDeleteCount > 253-1, throw a TypeError exception. + if (len + insertCount - actualDeleteCount > base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + // 12. Let A be ArraySpeciesCreate(O, actualDeleteCount). + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(actualDeleteCount)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + // 14. Let k be 0. + // 15. Repeat, while k < actualDeleteCount + // a. Let from be ToString(actualStart+k). + // b. Let fromPresent be HasProperty(O, from). + // d. If fromPresent is true, then + // i. Let fromValue be Get(O, from). + // iii. Let status be CreateDataPropertyOrThrow(A, ToString(k), fromValue). + // e. Increment k by 1. + JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < actualDeleteCount) { + double from = start + k; + fromKey.Update(JSTaggedValue(from)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + toKey.Update(JSTaggedValue(k)); + if (newArrayHandle->IsJSProxy()) { + toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue()); + } + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + } + // 16. Let setStatus be Set(A, "length", actualDeleteCount, true). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle actualDelcountHandle(thread, JSTaggedValue(actualDeleteCount)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, actualDelcountHandle, + true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 19. Let itemCount be the number of elements in items. + // 20. If itemCount < actualDeleteCount, then + // a. Let k be actualStart. + // b. Repeat, while k < (len – actualDeleteCount) + // i. Let from be ToString(k+actualDeleteCount). + // ii. Let to be ToString(k+itemCount). + // iii. Let fromPresent be HasProperty(O, from). + // v. If fromPresent is true, then + // 1. Let fromValue be Get(O, from). + // 3. Let setStatus be Set(O, to, fromValue, true). + // vi. Else fromPresent is false, + // 1. Let deleteStatus be DeletePropertyOrThrow(O, to). + // vii. Increase k by 1. + // c. Let k be len. + // d. Repeat, while k > (len – actualDeleteCount + itemCount) + // i. Let deleteStatus be DeletePropertyOrThrow(O, ToString(k–1)). + // iii. Decrease k by 1. + if (insertCount < actualDeleteCount) { + k = start; + while (k < len - actualDeleteCount) { + fromKey.Update(JSTaggedValue(k + actualDeleteCount)); + toKey.Update(JSTaggedValue(k + insertCount)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + } + k = len; + JSMutableHandle deleteKey(thread, JSTaggedValue::Undefined()); + while (k > len - actualDeleteCount + insertCount) { + deleteKey.Update(JSTaggedValue(k - 1)); + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, deleteKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k--; + } + } else if (insertCount > actualDeleteCount) { + // 21. Else if itemCount > actualDeleteCount, then + // a. Let k be (len – actualDeleteCount). + // b. Repeat, while k > actualStart + // i. Let from be ToString(k + actualDeleteCount – 1). + // ii. Let to be ToString(k + itemCount – 1) + // iii. Let fromPresent be HasProperty(O, from). + // iv. ReturnIfAbrupt(fromPresent). + // v. If fromPresent is true, then + // 1. Let fromValue be Get(O, from). + // 2. ReturnIfAbrupt(fromValue). + // 3. Let setStatus be Set(O, to, fromValue, true). + // 4. ReturnIfAbrupt(setStatus). + // vi. Else fromPresent is false, + // 1. Let deleteStatus be DeletePropertyOrThrow(O, to). + // 2. ReturnIfAbrupt(deleteStatus). + // vii. Decrease k by 1. + k = len - actualDeleteCount; + while (k > start) { + fromKey.Update(JSTaggedValue(k + actualDeleteCount - 1)); + toKey.Update(JSTaggedValue(k + insertCount - 1)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k--; + } + } + // 22. Let k be actualStart. + k = start; + // 23. Repeat, while items is not empty + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 2; i < argc; i++) { + JSHandle itemValue = GetCallArg(argv, i); + key.Update(JSTaggedValue(k)); + JSArray::FastSetPropertyByValue(thread, thisObjVal, key, itemValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // 24. Let setStatus be Set(O, "length", len – actualDeleteCount + itemCount, true). + double newLen = len - actualDeleteCount + insertCount; + JSHandle newLenHandle(thread, JSTaggedValue(newLen)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true); + // 25. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 26. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.3.26 Array.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ) +JSTaggedValue BuiltinsArray::ToLocaleString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, ToLocaleString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let separator be the String value for the list-separator String appropriate for the host environment’s + // current locale (this is derived in an implementation-defined way). + JSHandle sepHandle; + if ((GetCallArg(argv, 0)->IsUndefined())) { + sepHandle = JSHandle::Cast(ecmaVm->GetFactory()->NewFromString(",")); + } else { + sepHandle = GetCallArg(argv, 0); + } + JSHandle sepStringHandle = JSTaggedValue::ToString(thread, sepHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + CString sepString = ConvertToString(*sepStringHandle); + // 6. If len is zero, return the empty String. + if (len == 0) { + return GetTaggedString(thread, ""); + } + + // Inject locales and options argument into a taggedArray + JSHandle locales = GetCallArg(argv, 0); + JSHandle options = GetCallArg(argv, 1); + JSHandle argArray = factory->NewTaggedArray(2); // 2: length + argArray->Set(thread, 0, locales); + argArray->Set(thread, 1, options); + + CString concatStr; + // 7. Let firstElement be Get(array, "0"). + // 8. ReturnIfAbrupt(firstElement). + // 9. If firstElement is undefined or null, then + // a. Let R be the empty String. + // 10. Else + // a. Let R be ToString(Invoke(firstElement, "toLocaleString")). + // b. ReturnIfAbrupt(R). + // 11. Let k be 1. + // 12. Repeat, while k < len + // a. Let S be a String value produced by concatenating R and separator. + // b. Let nextElement be Get(array, ToString(k)). + // c. ReturnIfAbrupt(nextElement). + // d. If nextElement is undefined or null, then + // i. Let R be the empty String. + // e. Else + // i. Let R be ToString(Invoke(nextElement, "toLocaleString")). + // ii. ReturnIfAbrupt(R). + // f. Let R be a String value produced by concatenating S and R. + // g. Increase k by 1. + auto globalConst = thread->GlobalConstants(); + for (int32_t k = 0; k < len; k++) { + JSTaggedValue next = globalConst->GetEmptyString(); + JSHandle nextElement = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!nextElement->IsUndefined() && !nextElement->IsNull()) { + JSHandle nextValueHandle = nextElement; + JSTaggedValue callResult = + JSFunction::Invoke(thread, nextValueHandle, globalConst->GetHandledToLocaleStringString(), argArray); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + next = callResult; + } + JSHandle nextHandle(thread, next); + JSHandle nextStringHandle = JSTaggedValue::ToString(thread, nextHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + CString nextString = ConvertToString(*nextStringHandle); + if (k > 0) { + concatStr += sepString; + concatStr += nextString; + continue; + } + concatStr += nextString; + } + + // 13. Return R. + return factory->NewFromString(concatStr).GetTaggedValue(); +} + +// 22.1.3.27 Array.prototype.toString ( ) +JSTaggedValue BuiltinsArray::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let array be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(array). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let func be Get(array, "join"). + JSHandle key(factory->NewFromString("join")); + JSHandle callbackFnHandle(JSTaggedValue::GetProperty(thread, thisObjVal, key).GetValue()); + + // 4. ReturnIfAbrupt(func). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(func) is false, let func be the intrinsic function %ObjProto_toString% (19.1.3.6). + if (!callbackFnHandle->IsCallable()) { + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + JSHandle objectPrototype = env->GetObjectFunctionPrototype(); + JSHandle tostring = thread->GlobalConstants()->GetHandledToStringString(); + JSHandle callbackfnHandle = + JSHandle::Cast(JSTaggedValue::GetProperty(thread, objectPrototype, tostring).GetValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSFunction::Call(thread, callbackfnHandle, thisObjVal, BuiltinsBase::GetArgsArray(argv)); + } + + return JSFunction::Call(thread, callbackFnHandle, thisObjVal, GetArgsArray(argv)); +} + +// 22.1.3.28 Array.prototype.unshift ( ...items ) +JSTaggedValue BuiltinsArray::Unshift(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Unshift); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 5. Let argCount be the number of actual arguments. + array_size_t argc = argv->GetArgsNumber(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. If argCount > 0, then + // a. If len+ argCount > 253-1, throw a TypeError exception. + // b. Let k be len. + // c. Repeat, while k > 0, + // i. Let from be ToString(k–1). + // ii. Let to be ToString(k+argCount –1). + // iii. Let fromPresent be HasProperty(O, from). + // iv. ReturnIfAbrupt(fromPresent). + // v. If fromPresent is true, then + // 1. Let fromValue be Get(O, from). + // 2. ReturnIfAbrupt(fromValue). + // 3. Let setStatus be Set(O, to, fromValue, true). + // 4. ReturnIfAbrupt(setStatus). + // vi. Else fromPresent is false, + // 1. Let deleteStatus be DeletePropertyOrThrow(O, to). + // 2. ReturnIfAbrupt(deleteStatus). + // vii. Decrease k by 1. + if (argc > 0) { + if (len + argc > base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + double k = len; + while (k > 0) { + fromKey.Update(JSTaggedValue(k - 1)); + toKey.Update(JSTaggedValue(k + argc - 1)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k--; + } + // d. Let j be 0. + // e. Let items be a List whose elements are, in left to right order, the arguments that were passed to this + // function invocation. + // f. Repeat, while items is not empty + // i. Remove the first element from items and let E be the value of that element. + // ii. Let setStatus be Set(O, ToString(j), E, true). + // iii. ReturnIfAbrupt(setStatus). + // iv. Increase j by 1. + double j = 0; + while (j < argc) { + toKey.Update(JSTaggedValue(j)); + JSHandle toValue = GetCallArg(argv, j); + JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, toValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + j++; + } + } + + // 7. Let setStatus be Set(O, "length", len+argCount, true). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + double newLen = len + argc; + JSHandle newLenHandle(thread, JSTaggedValue(newLen)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true); + // 8. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 9. Return len+argCount. + return GetTaggedDouble(newLen); +} + +// 22.1.3.29 Array.prototype.values ( ) +JSTaggedValue BuiltinsArray::Values(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Values); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let O be ToObject(this value). + // 2. ReturnIfAbrupt(O). + JSHandle self = JSTaggedValue::ToObject(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return CreateArrayIterator(O, "value"). + JSHandle iter(factory->NewJSArrayIterator(self, IterationKind::VALUE)); + return iter.GetTaggedValue(); +} +// 22.1.3.31 Array.prototype [ @@unscopables ] +JSTaggedValue BuiltinsArray::Unscopables(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle unscopableList = factory->OrdinaryNewJSObjectCreate(nullHandle); + + JSHandle trueVal(thread, JSTaggedValue::True()); + JSHandle copyWithKey(factory->NewFromString("copyWithin")); + JSObject::CreateDataProperty(thread, unscopableList, copyWithKey, trueVal); + + JSHandle entriesKey(factory->NewFromString("entries")); + JSObject::CreateDataProperty(thread, unscopableList, entriesKey, trueVal); + + JSHandle fillKey(factory->NewFromString("fill")); + JSObject::CreateDataProperty(thread, unscopableList, fillKey, trueVal); + + JSHandle findKey(factory->NewFromString("find")); + JSObject::CreateDataProperty(thread, unscopableList, findKey, trueVal); + + JSHandle findIndexKey(factory->NewFromString("findIndex")); + JSObject::CreateDataProperty(thread, unscopableList, findIndexKey, trueVal); + + JSHandle flatKey(factory->NewFromString("flat")); + JSObject::CreateDataProperty(thread, unscopableList, flatKey, trueVal); + + JSHandle flatMapKey(factory->NewFromString("flatMap")); + JSObject::CreateDataProperty(thread, unscopableList, flatMapKey, trueVal); + + JSHandle includesKey(factory->NewFromString("includes")); + JSObject::CreateDataProperty(thread, unscopableList, includesKey, trueVal); + + JSHandle keysKey(factory->NewFromString("keys")); + JSObject::CreateDataProperty(thread, unscopableList, keysKey, trueVal); + + JSHandle valuesKey(factory->NewFromString("values")); + JSObject::CreateDataProperty(thread, unscopableList, valuesKey, trueVal); + + return unscopableList.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_array.h b/ecmascript/builtins/builtins_array.h new file mode 100644 index 0000000000000000000000000000000000000000..ee9027712ab38568c9e176282108bde38a6c98d9 --- /dev/null +++ b/ecmascript/builtins/builtins_array.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ARRAY_H + +#include "ecmascript/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +static constexpr uint8_t INDEX_TWO = 2; +static constexpr uint8_t INDEX_THREE = 3; +class BuiltinsArray : public base::BuiltinsBase { +public: + // 22.1.1 + static JSTaggedValue ArrayConstructor(EcmaRuntimeCallInfo *argv); + + // 22.1.2.1 + static JSTaggedValue From(EcmaRuntimeCallInfo *argv); + // 22.1.2.2 + static JSTaggedValue IsArray(EcmaRuntimeCallInfo *argv); + // 22.1.2.3 + static JSTaggedValue Of(EcmaRuntimeCallInfo *argv); + // 22.1.2.5 + static JSTaggedValue Species(EcmaRuntimeCallInfo *argv); + + // prototype + // 22.1.3.1 + static JSTaggedValue Concat(EcmaRuntimeCallInfo *argv); + // 22.1.3.3 + static JSTaggedValue CopyWithin(EcmaRuntimeCallInfo *argv); + // 22.1.3.4 + static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); + // 22.1.3.5 + static JSTaggedValue Every(EcmaRuntimeCallInfo *argv); + // 22.1.3.6 + static JSTaggedValue Fill(EcmaRuntimeCallInfo *argv); + // 22.1.3.7 + static JSTaggedValue Filter(EcmaRuntimeCallInfo *argv); + // 22.1.3.8 + static JSTaggedValue Find(EcmaRuntimeCallInfo *argv); + // 22.1.3.9 + static JSTaggedValue FindIndex(EcmaRuntimeCallInfo *argv); + // 22.1.3.10 + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + // 22.1.3.11 + static JSTaggedValue IndexOf(EcmaRuntimeCallInfo *argv); + // 22.1.3.12 + static JSTaggedValue Join(EcmaRuntimeCallInfo *argv); + // 22.1.3.13 + static JSTaggedValue Keys(EcmaRuntimeCallInfo *argv); + // 22.1.3.14 + static JSTaggedValue LastIndexOf(EcmaRuntimeCallInfo *argv); + // 22.1.3.15 + static JSTaggedValue Map(EcmaRuntimeCallInfo *argv); + // 22.1.3.16 + static JSTaggedValue Pop(EcmaRuntimeCallInfo *argv); + // 22.1.3.17 + static JSTaggedValue Push(EcmaRuntimeCallInfo *argv); + // 22.1.3.18 + static JSTaggedValue Reduce(EcmaRuntimeCallInfo *argv); + // 22.1.3.19 + static JSTaggedValue ReduceRight(EcmaRuntimeCallInfo *argv); + // 22.1.3.20 + static JSTaggedValue Reverse(EcmaRuntimeCallInfo *argv); + // 22.1.3.21 + static JSTaggedValue Shift(EcmaRuntimeCallInfo *argv); + // 22.1.3.22 + static JSTaggedValue Slice(EcmaRuntimeCallInfo *argv); + // 22.1.3.23 + static JSTaggedValue Some(EcmaRuntimeCallInfo *argv); + // 22.1.3.24 + static JSTaggedValue Sort(EcmaRuntimeCallInfo *argv); + // 22.1.3.25 + static JSTaggedValue Splice(EcmaRuntimeCallInfo *argv); + // 22.1.3.26 + static JSTaggedValue ToLocaleString(EcmaRuntimeCallInfo *argv); + // 22.1.3.27 + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 22.1.3.28 + static JSTaggedValue Unshift(EcmaRuntimeCallInfo *argv); + // 22.1.3.29 + static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); + // 22.1.3.31 + static JSTaggedValue Unscopables(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins + +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ARRAY_H \ No newline at end of file diff --git a/ecmascript/builtins/builtins_arraybuffer.cpp b/ecmascript/builtins/builtins_arraybuffer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aec84a67099722cd3d9284fe58a22e853aacbec2 --- /dev/null +++ b/ecmascript/builtins/builtins_arraybuffer.cpp @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_arraybuffer.h" + +#include + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/base/number_helper.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "securec.h" + +namespace panda::ecmascript::builtins { +// 24.1.2.1 ArrayBuffer(length) +JSTaggedValue BuiltinsArrayBuffer::ArrayBufferConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle newTarget = GetNewTarget(argv); + // 1. If NewTarget is undefined, throw a TypeError exception. + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "newtarget is undefined", JSTaggedValue::Exception()); + } + JSHandle lengthHandle = GetCallArg(argv, 0); + JSTaggedNumber lenNum = JSTaggedValue::ToIndex(thread, lengthHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double length = lenNum.GetNumber(); + return AllocateArrayBuffer(thread, newTarget, length); +} + +// 24.1.3.1 ArrayBuffer.isView(arg) +JSTaggedValue BuiltinsArrayBuffer::IsView(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + JSHandle arg = GetCallArg(argv, 0); + // 1. If Type(arg) is not Object, return false. + if (!arg->IsECMAObject()) { + return BuiltinsArrayBuffer::GetTaggedBoolean(false); + } + // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true. + if (arg->IsDataView() || arg->IsTypedArray()) { + return BuiltinsArrayBuffer::GetTaggedBoolean(true); + } + // 3. Return false. + return BuiltinsArrayBuffer::GetTaggedBoolean(false); +} + +// 24.1.3.3 get ArrayBuffer [ @@species ] +JSTaggedValue BuiltinsArrayBuffer::Species(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetThis(argv).GetTaggedValue(); +} + +// 24.1.4.1 get ArrayBuffer.prototype.byteLength +JSTaggedValue BuiltinsArrayBuffer::GetByteLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + JSThread *thread = argv->GetThread(); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception()); + } + // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. + if (!thisHandle->IsArrayBuffer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception()); + } + // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if (IsDetachedBuffer(thisHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "IsDetachedBuffer", JSTaggedValue::Exception()); + } + JSHandle arrBuf(thisHandle); + // 5. Let length be the value of O’s [[ArrayBufferByteLength]] internal slot. + JSTaggedValue length = arrBuf->GetArrayBufferByteLength(); + // 6. Return length. + return JSTaggedValue(length); +} + +// 24.1.4.3 ArrayBuffer.prototype.slice(start, end) +JSTaggedValue BuiltinsArrayBuffer::Slice(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Slice); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception()); + } + JSHandle arrBuf(thisHandle); + // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. + if (!thisHandle->IsArrayBuffer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception()); + } + // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if (IsDetachedBuffer(thisHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception()); + } + // 5. Let len be the value of O’s [[ArrayBufferByteLength]] internal slot. + JSTaggedNumber lengthNum = JSTaggedNumber::FromIntOrDouble(thread, arrBuf->GetArrayBufferByteLength()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle startHandle = GetCallArg(argv, 0); + // 6. Let relativeStart be ToInteger(start). + JSTaggedNumber relativeStart = JSTaggedValue::ToInteger(thread, startHandle); + // 7. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t len = lengthNum.ToInt32(); + int32_t start = base::NumberHelper::DoubleInRangeInt32(relativeStart.GetNumber()); + int32_t end; + int32_t first; + int32_t last; + // 8. If relativeStart < 0, let first be max((len + relativeStart),0); else let first be min(relativeStart, len). + if (start < 0) { + first = std::max((len + start), 0); + } else { + first = std::min(start, len); + } + // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end). + JSHandle endHandle = GetCallArg(argv, 1); + if (endHandle->IsUndefined()) { + end = len; + } else { + JSTaggedNumber relativeEnd = JSTaggedValue::ToInteger(thread, endHandle); + // 10. ReturnIfAbrupt(relativeEnd). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + end = base::NumberHelper::DoubleInRangeInt32(relativeEnd.GetNumber()); + } + // 11. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len). + if (end < 0) { + last = std::max((len + end), 0); + } else { + last = std::min(end, len); + } + // 12. Let newLen be max(final-first,0). + int32_t newLen = std::max((last - first), 0); + // 13. Let ctor be SpeciesConstructor(O, %ArrayBuffer%). + JSHandle defaultConstructor = env->GetArrayBufferFunction(); + JSHandle objHandle(thisHandle); + JSHandle constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor); + // 14. ReturnIfAbrupt(ctor). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 15. Let new be Construct(ctor, «newLen»). + JSHandle undefined = globalConst->GetHandledUndefined(); + JSHandle arguments = factory->NewTaggedArray(1); + arguments->Set(thread, 0, JSTaggedValue(newLen)); + JSTaggedValue taggedNewArrBuf = JSFunction::Construct(thread, constructor, arguments, undefined); + JSHandle newArrBuf(thread, taggedNewArrBuf); + // 16. ReturnIfAbrupt(new). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 17. If new does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. + if (!newArrBuf->IsArrayBuffer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "don't have bufferdata internal slot", JSTaggedValue::Exception()); + } + // 18. If IsDetachedBuffer(new) is true, throw a TypeError exception. + if (IsDetachedBuffer(newArrBuf.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new arrayBuffer IsDetachedBuffer", JSTaggedValue::Exception()); + } + // 19. If SameValue(new, O) is true, throw a TypeError exception. + if (JSTaggedValue::SameValue(newArrBuf.GetTaggedValue(), thisHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "value of new arraybuffer and this is same", JSTaggedValue::Exception()); + } + JSHandle newJsArrBuf(newArrBuf); + // 20. If the value of new’s [[ArrayBufferByteLength]] internal slot < newLen, throw a TypeError exception. + JSTaggedNumber newLengthNum = JSTaggedNumber::FromIntOrDouble(thread, newJsArrBuf->GetArrayBufferByteLength()); + int32_t newArrBufLen = newLengthNum.ToInt32(); + if (newArrBufLen < newLen) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new array buffer length smaller than newlen", JSTaggedValue::Exception()); + } + // 21. NOTE: Side-effects of the above steps may have detached O. + // 22. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if (IsDetachedBuffer(thisHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception()); + } + if (newLen > 0) { + // 23. Let fromBuf be the value of O’s [[ArrayBufferData]] internal slot. + JSTaggedValue from = arrBuf->GetArrayBufferData(); + // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot. + JSTaggedValue to = newJsArrBuf->GetArrayBufferData(); + // 25. Perform CopyDataBlockBytes(toBuf, fromBuf, first, newLen). + JSArrayBuffer::CopyDataBlockBytes(to, from, first, newLen); + } + // Return new. + return newArrBuf.GetTaggedValue(); +} + +// 24.1.1.1 AllocateArrayBuffer(constructor, byteLength) +JSTaggedValue BuiltinsArrayBuffer::AllocateArrayBuffer(JSThread *thread, const JSHandle &newTarget, + double byteLength) +{ + BUILTINS_API_TRACE(thread, ArrayBuffer, AllocateArrayBuffer); + /** + * 1. Let obj be OrdinaryCreateFromConstructor(constructor, "%ArrayBufferPrototype%", + * «[[ArrayBufferData]], [[ArrayBufferByteLength]]» ). + * */ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrBufFunc = env->GetArrayBufferFunction(); + JSHandle obj; + if (!newTarget->IsBoundFunction()) { + obj = factory->NewJSObjectByConstructor(JSHandle(arrBufFunc), newTarget); + // 2. ReturnIfAbrupt + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + JSHandle prototypeKey = thread->GlobalConstants()->GetHandledPrototypeString(); + JSHandle constructTag(newTarget); + JSHandle constructProto = + JSTaggedValue::GetProperty(thread, constructTag, prototypeKey).GetValue(); + obj = JSObject::ObjectCreate(thread, JSHandle(constructProto)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 3. Assert: byteLength is a positive integer. + ASSERT(JSTaggedValue(byteLength).IsInteger()); + ASSERT(byteLength >= 0); + // 4. Let block be CreateByteDataBlock(byteLength). + if (byteLength > INT_MAX) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Out of range", JSTaggedValue::Exception()); + } + JSHandle arrayBuffer(obj); + // 6. Set obj’s [[ArrayBufferData]] internal slot to block. + factory->NewJSArrayBufferData(arrayBuffer, byteLength); + // 7. Set obj’s [[ArrayBufferByteLength]] internal slot to byteLength. + arrayBuffer->SetArrayBufferByteLength(thread, JSTaggedValue(static_cast(byteLength))); + // 8. Return obj. + return arrayBuffer.GetTaggedValue(); +} + +// 24.1.1.2 IsDetachedBuffer() +bool BuiltinsArrayBuffer::IsDetachedBuffer(JSTaggedValue arrayBuffer) +{ + // 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot. + ASSERT(arrayBuffer.IsArrayBuffer()); + JSArrayBuffer *buffer = JSArrayBuffer::Cast(arrayBuffer.GetTaggedObject()); + JSTaggedValue dataSlot = buffer->GetArrayBufferData(); + // 2. If arrayBuffer’s [[ArrayBufferData]] internal slot is null, return true. + // 3. Return false. + return dataSlot == JSTaggedValue::Null(); +} + +// 24.1.1.4 +JSTaggedValue BuiltinsArrayBuffer::CloneArrayBuffer(JSThread *thread, const JSHandle &srcBuffer, + int32_t srcByteOffset, JSHandle constructor) +{ + BUILTINS_API_TRACE(thread, ArrayBuffer, CloneArrayBuffer); + // 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot. + ASSERT(srcBuffer->IsArrayBuffer()); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // 2. If cloneConstructor is not present + if (constructor->IsUndefined()) { + // a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%). + JSHandle defaultConstructor = env->GetArrayBufferFunction(); + JSHandle objHandle(srcBuffer); + constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor); + // b. ReturnIfAbrupt(cloneConstructor). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } else { + ASSERT(constructor->IsConstructor()); + } + } + // 4. Let srcLength be the value of srcBuffer’s [[ArrayBufferByteLength]] internal slot. + JSHandle arrBuf(srcBuffer); + JSTaggedNumber lengthNumber = JSTaggedNumber::FromIntOrDouble(thread, arrBuf->GetArrayBufferByteLength()); + int32_t srcLen = lengthNumber.ToInt32(); + // 5. Assert: srcByteOffset ≤ srcLength. + ASSERT(srcByteOffset <= srcLen); + // 6. Let cloneLength be srcLength – srcByteOffset. + int32_t cloneLen = srcLen - srcByteOffset; + // 8. Let targetBuffer be AllocateArrayBuffer(cloneConstructor, cloneLength). + JSTaggedValue taggedBuf = AllocateArrayBuffer(thread, constructor, cloneLen); + // 9. ReturnIfAbrupt(targetBuffer). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 10. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } + // 11. Let targetBlock be the value of targetBuffer’s [[ArrayBufferData]] internal slot. + JSHandle newArrBuf(thread, taggedBuf); + // Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength). + // 7. Let srcBlock be the value of srcBuffer’s [[ArrayBufferData]] internal slot. + JSTaggedValue srcBlock = arrBuf->GetArrayBufferData(); + JSTaggedValue targetBlock = newArrBuf->GetArrayBufferData(); + if (cloneLen > 0) { + JSArrayBuffer::CopyDataBlockBytes(targetBlock, srcBlock, srcByteOffset, cloneLen); + } + return taggedBuf; +} + +// 24.1.1.5 +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSTaggedValue arrBuf, int32_t byteIndex, DataViewType type, + bool littleEndian) +{ + JSArrayBuffer *jsArrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject()); + JSTaggedValue data = jsArrayBuffer->GetArrayBufferData(); + void *pointer = JSNativePointer::Cast(data.GetTaggedObject())->GetExternalPointer(); + auto *block = reinterpret_cast(pointer); + switch (type) { + case DataViewType::UINT8: + case DataViewType::UINT8_CLAMPED: { + uint8_t res = block[byteIndex]; // NOLINT + return GetTaggedInt(res); + } + case DataViewType::INT8: { + uint8_t res = block[byteIndex]; // NOLINT + auto int8Res = static_cast(res); + return GetTaggedInt(int8Res); + } + case DataViewType::UINT16: + return GetValueFromBufferForInteger(block, byteIndex, littleEndian); + case DataViewType::INT16: + return GetValueFromBufferForInteger(block, byteIndex, littleEndian); + case DataViewType::UINT32: + return GetValueFromBufferForInteger(block, byteIndex, littleEndian); + case DataViewType::INT32: + return GetValueFromBufferForInteger(block, byteIndex, littleEndian); + case DataViewType::FLOAT32: + return GetValueFromBufferForFloat(block, byteIndex, littleEndian); + case DataViewType::FLOAT64: + return GetValueFromBufferForFloat(block, byteIndex, littleEndian); + default: + break; + } + + UNREACHABLE(); +} + +// 24.1.1.6 +JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSTaggedValue arrBuf, int32_t byteIndex, DataViewType type, + JSTaggedNumber value, bool littleEndian) +{ + JSArrayBuffer *jsArrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject()); + JSTaggedValue data = jsArrayBuffer->GetArrayBufferData(); + void *pointer = JSNativePointer::Cast(data.GetTaggedObject())->GetExternalPointer(); + auto *block = reinterpret_cast(pointer); + double val = value.GetNumber(); + switch (type) { + case DataViewType::UINT8: + SetValueInBufferForByte(val, block, byteIndex); + break; + case DataViewType::UINT8_CLAMPED: + SetValueInBufferForUint8Clamped(val, block, byteIndex); + break; + case DataViewType::INT8: + SetValueInBufferForByte(val, block, byteIndex); + break; + case DataViewType::UINT16: + SetValueInBufferForInteger(val, block, byteIndex, littleEndian); + break; + case DataViewType::INT16: + SetValueInBufferForInteger(val, block, byteIndex, littleEndian); + break; + case DataViewType::UINT32: + SetValueInBufferForInteger(val, block, byteIndex, littleEndian); + break; + case DataViewType::INT32: + SetValueInBufferForInteger(val, block, byteIndex, littleEndian); + break; + case DataViewType::FLOAT32: + SetValueInBufferForFloat(val, block, byteIndex, littleEndian); + break; + case DataViewType::FLOAT64: + SetValueInBufferForFloat(val, block, byteIndex, littleEndian); + break; + default: + UNREACHABLE(); + } + return JSTaggedValue::Undefined(); +} + +template +void BuiltinsArrayBuffer::SetTypeData(uint8_t *block, T value, int32_t index) +{ + int32_t sizeCount = sizeof(T); + auto *res = reinterpret_cast(&value); + for (int i = 0; i < sizeCount; i++) { + *(block + index + i) = *(res + i); // NOLINT + } +} + +template +T BuiltinsArrayBuffer::TransformIntToBigEndian(T liValue) +{ + uint8_t sizeCount = sizeof(T); + T biValue; + if (sizeCount == 2) { // 2:2 means size of T + biValue = ((liValue & 0x00FF) << BITS_EIGHT) // NOLINT + | ((liValue & 0xFF00) >> BITS_EIGHT); // NOLINT + } else { + biValue = ((liValue & 0x000000FF) << BITS_TWENTY_FOUR) // NOLINT + | ((liValue & 0x0000FF00) << BITS_EIGHT) // NOLINT + | ((liValue & 0x00FF0000) >> BITS_EIGHT) // NOLINT + | ((liValue & 0xFF000000) >> BITS_TWENTY_FOUR); // NOLINT + } + return biValue; +} + +template +T BuiltinsArrayBuffer::TransformFloatToBigEndian(T fValue) +{ + static_assert(std::is_same_v || std::is_same_v, "T must be float type"); + constexpr int32_t size = sizeof(T); + uint8_t s[size]; // NOLINT + uint8_t t[size]; // NOLINT + if (memcpy_s(s, size, &fValue, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + for (int i = 0, j = size - 1; i < size && j >= 0; i++, j--) { + t[i] = s[j]; + } + T res; + if (memcpy_s(&res, size, t, size) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + return res; +} + +template +T BuiltinsArrayBuffer::TransformByteToNumber(uint8_t *block, int32_t index, uint8_t *tmpArr, int32_t size) +{ + if (memcpy_s(tmpArr, size, block + index, size) != EOK) { // NOLINT + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + auto *arr = reinterpret_cast(tmpArr); + T res = *arr; + return res; +} + +template +JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, int32_t byteIndex, bool littleEndian) +{ + static_assert(std::is_integral_v, "T must be integral"); + static_assert(sizeof(T) == size, "Invalid number size"); + static_assert(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8"); + + ASSERT(size >= NumberSize::UINT16 || size <= NumberSize::FLOAT64); + uint8_t tmpArr[size]; // NOLINT + int32_t capacity = sizeof(T); + auto res = TransformByteToNumber(block, byteIndex, tmpArr, capacity); + if (!littleEndian) { + res = TransformIntToBigEndian(res); + } + + // uint32_t maybe overflow with TaggedInt + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + if constexpr (std::is_same_v) { + // NOLINTNEXTLINE(clang-diagnostic-sign-compare) + if (res > static_cast(std::numeric_limits::max())) { + return GetTaggedDouble(static_cast(res)); + } + } + return GetTaggedInt(res); +} + +template +JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForFloat(uint8_t *block, int32_t byteIndex, bool littleEndian) +{ + static_assert(std::is_same_v || std::is_same_v, "T must be float type"); + static_assert(sizeof(T) == size, "Invalid number size"); + uint8_t tmpArr[size]; // NOLINT + int32_t capacity = sizeof(T); + auto tmp = TransformByteToNumber(block, byteIndex, tmpArr, capacity); + + // NOLINTNEXTLINE(readability-braces-around-statements) + if constexpr (std::is_same_v) { + if (std::isnan(tmp)) { + return GetTaggedDouble(tmp); + } + } else if constexpr (std::is_same_v) { // NOLINTNEXTLINE(readability-braces-around-statements) + if (std::isnan(tmp) && !JSTaggedValue::IsImpureNaN(tmp)) { + return GetTaggedDouble(tmp); + } + } + + if (!littleEndian) { + T res = TransformFloatToBigEndian(tmp); + return GetTaggedDouble(res); + } + return GetTaggedDouble(tmp); +} + +template +void BuiltinsArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, int32_t byteIndex) +{ + static_assert(std::is_same_v || std::is_same_v, "T must be int8/uint8"); + T res; + if (std::isnan(val) || std::isinf(val)) { + res = 0; + SetTypeData(block, res, byteIndex); + return; + } + auto int64Val = static_cast(val); + auto *resArr = reinterpret_cast(&int64Val); + res = *resArr; + SetTypeData(block, res, byteIndex); +} + +void BuiltinsArrayBuffer::SetValueInBufferForUint8Clamped(double val, uint8_t *block, int32_t byteIndex) +{ + uint8_t res; + if (std::isnan(val) || val <= 0) { + res = 0; + SetTypeData(block, res, byteIndex); + return; + } + val = val >= UINT8_MAX ? UINT8_MAX : val; + constexpr double HALF = 0.5; + val = val == HALF ? 0 : std::round(val); + res = static_cast(val); + SetTypeData(block, res, byteIndex); +} + +template +void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, int32_t byteIndex, bool littleEndian) +{ + static_assert(std::is_integral_v, "T must be integral"); + static_assert(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8"); + T res; + if (std::isnan(val) || std::isinf(val)) { + res = 0; + SetTypeData(block, res, byteIndex); + return; + } + auto int64Val = static_cast(val); + // NOLINTNEXTLINE(readability-braces-around-statements) + if constexpr (std::is_same_v) { + auto *pTmp = reinterpret_cast(&int64Val); + int16_t tmp = *pTmp; + res = static_cast(tmp); + } else { // NOLINTNEXTLINE(readability-braces-around-statements) + auto *pTmp = reinterpret_cast(&int64Val); + res = *pTmp; + } + + if (!littleEndian) { + res = TransformIntToBigEndian(res); + } + SetTypeData(block, res, byteIndex); +} + +template +void BuiltinsArrayBuffer::SetValueInBufferForFloat(double val, uint8_t *block, int32_t byteIndex, bool littleEndian) +{ + static_assert(std::is_same_v || std::is_same_v, "T must be float type"); + auto data = static_cast(val); + if (std::isnan(val)) { + SetTypeData(block, data, byteIndex); + return; + } + if (!littleEndian) { + auto res = TransformFloatToBigEndian(data); + SetTypeData(block, res, byteIndex); + return; + } + SetTypeData(block, data, byteIndex); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_arraybuffer.h b/ecmascript/builtins/builtins_arraybuffer.h new file mode 100644 index 0000000000000000000000000000000000000000..5563dfe71a82d081a1c8993593a97712c2e4b93f --- /dev/null +++ b/ecmascript/builtins/builtins_arraybuffer.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ARRAYBUFFER_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ARRAYBUFFER_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/base/number_helper.h" +#include "ecmascript/js_dataview.h" + +namespace panda::ecmascript::builtins { +static constexpr double NUMBER_HALF = 0.5; +static constexpr uint32_t BITS_EIGHT = 8; +static constexpr uint32_t BITS_TWENTY_FOUR = 24; +using DataViewType = ecmascript::DataViewType; +class BuiltinsArrayBuffer : public base::BuiltinsBase { +public: + enum NumberSize : uint8_t { UINT16 = 2, INT16 = 2, UINT32 = 4, INT32 = 4, FLOAT32 = 4, FLOAT64 = 8 }; + + // 24.1.2.1 ArrayBuffer(length) + static JSTaggedValue ArrayBufferConstructor(EcmaRuntimeCallInfo *argv); + // 24.1.3.1 ArrayBuffer.isView(arg) + static JSTaggedValue IsView(EcmaRuntimeCallInfo *argv); + // 24.1.3.3 get ArrayBuffer[@@species] + static JSTaggedValue Species(EcmaRuntimeCallInfo *argv); + // 24.1.4.1 get ArrayBuffer.prototype.byteLength + static JSTaggedValue GetByteLength(EcmaRuntimeCallInfo *argv); + // 24.1.4.3 ArrayBuffer.prototype.slice() + static JSTaggedValue Slice(EcmaRuntimeCallInfo *argv); + // 24.1.1.2 IsDetachedBuffer(arrayBuffer) + static bool IsDetachedBuffer(JSTaggedValue arrayBuffer); + // 24.1.1.5 GetValueFromBuffer ( arrayBuffer, byteIndex, type, isLittleEndian ) + static JSTaggedValue GetValueFromBuffer(JSTaggedValue arrBuf, int32_t byteIndex, DataViewType type, + bool littleEndian); + // 24.1.1.6 SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isLittleEndian ) + static JSTaggedValue SetValueInBuffer(JSTaggedValue arrBuf, int32_t byteIndex, DataViewType type, + JSTaggedNumber value, bool littleEndian); + // 24.1.1.4 CloneArrayBuffer( srcBuffer, srcByteOffset [, cloneConstructor] ) + static JSTaggedValue CloneArrayBuffer(JSThread *thread, const JSHandle &srcBuffer, + int32_t srcByteOffset, JSHandle constructor); + // 24.1.1.1 AllocateArrayBuffer(constructor, byteLength) + static JSTaggedValue AllocateArrayBuffer(JSThread *thread, const JSHandle &newTarget, + double byteLength); + +private: + template + static T TransformIntToBigEndian(T liValue); + + template + static T TransformFloatToBigEndian(T fValue); + + template + static T TransformByteToNumber(uint8_t *block, int32_t index, uint8_t *tmpArr, int32_t size); + + template + static void SetTypeData(uint8_t *block, T value, int32_t index); + + template + static JSTaggedValue GetValueFromBufferForInteger(uint8_t *block, int32_t byteIndex, bool littleEndian); + + template + static JSTaggedValue GetValueFromBufferForFloat(uint8_t *block, int32_t byteIndex, bool littleEndian); + + template + static void SetValueInBufferForByte(double val, uint8_t *block, int32_t byteIndex); + + static void SetValueInBufferForUint8Clamped(double val, uint8_t *block, int32_t byteIndex); + + template + static void SetValueInBufferForInteger(double val, uint8_t *block, int32_t byteIndex, bool littleEndian); + + template + static void SetValueInBufferForFloat(double val, uint8_t *block, int32_t byteIndex, bool littleEndian); +}; +} // namespace panda::ecmascript::builtins + +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ARRAYBUFFER_H \ No newline at end of file diff --git a/ecmascript/builtins/builtins_async_function.cpp b/ecmascript/builtins/builtins_async_function.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5c57ad10bdf241c5e1689de15bdc8d615ff71046 --- /dev/null +++ b/ecmascript/builtins/builtins_async_function.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_async_function.h" +#include "ecmascript/ecma_macros.h" + +namespace panda::ecmascript::builtins { +// ecma2017 25.5.1.1 AsyncFunction (p1, p2, ... , pn, body) +JSTaggedValue BuiltinsAsyncFunction::AsyncFunctionConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // not support + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "Not support eval. Forbidden using new AsyncFunction().", + JSTaggedValue::Exception()); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_async_function.h b/ecmascript/builtins/builtins_async_function.h new file mode 100644 index 0000000000000000000000000000000000000000..23174569f7fc667354dda44550f51c2c4170601b --- /dev/null +++ b/ecmascript/builtins/builtins_async_function.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ASYNC_FUNCTION_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ASYNC_FUNCTION_H + +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsAsyncFunction : public base::BuiltinsBase { +public: + // ecma2020 25.5.1.1 AsyncFunction (p1, p2, ... , pn, body) + static JSTaggedValue AsyncFunctionConstructor(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ASYNC_FUNCTION_H diff --git a/ecmascript/builtins/builtins_boolean.cpp b/ecmascript/builtins/builtins_boolean.cpp new file mode 100644 index 0000000000000000000000000000000000000000..616af10df6fd965a3b0cbe3417228501f460d415 --- /dev/null +++ b/ecmascript/builtins/builtins_boolean.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_boolean.h" + +#include "ecmascript/builtins/builtins_errors.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_primitive_ref.h" + +namespace panda::ecmascript::builtins { +// ecma 19.3.1.1 Boolean(value) +JSTaggedValue BuiltinsBoolean::BooleanConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Boolean, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let b be ToBoolean(value). + bool boolValue = GetCallArg(argv, 0)->ToBoolean(); + // 2. If NewTarget is undefined, return b. + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + return GetTaggedBoolean(boolValue); + } + // 3. Let O be OrdinaryCreateFromConstructor(NewTarget, "%BooleanPrototype%", [[BooleanData]] ). + // 5. Set the value of O's [[BooleanData]] internal slot to b. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle ctor = JSHandle(GetConstructor(argv)); + JSHandle result = factory->NewJSObjectByConstructor(ctor, newTarget); + JSTaggedValue objValue = boolValue ? JSTaggedValue::True() : JSTaggedValue::False(); + JSPrimitiveRef::Cast(*result)->SetValue(thread, objValue); + // 4. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 6. Return O. + return result.GetTaggedValue(); +} + +// ecma 19.3.3 abstract operation thisBooleanValue(value) +JSTaggedValue BuiltinsBoolean::ThisBooleanValue(JSThread *thread, JSTaggedValue value) +{ + BUILTINS_API_TRACE(thread, Boolean, ThisBooleanValue); + // 1. If Type(value) is Boolean, return value + if (value.IsBoolean()) { + return value == JSTaggedValue::True() ? GetTaggedBoolean(true) : GetTaggedBoolean(false); + } + // 2. If Type(value) is Object and value has a [[BooleanData]] internal slot, then + if (value.IsJSPrimitiveRef()) { + JSTaggedValue primitive = JSPrimitiveRef::Cast(value.GetTaggedObject())->GetValue(); + // a. Assert: value's [[BooleanData]] internal slot is a Boolean value. + if (primitive.IsBoolean()) { + // b. Return the value of value's [[BooleanData]] internal slot. + return primitive == JSTaggedValue::True() ? GetTaggedBoolean(true) : GetTaggedBoolean(false); + } + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 3. Throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "the type can not convert to BooleanValue", JSTaggedValue::Exception()); +} + +// ecma 19.3.3.2 Boolean.prototype.toString () +JSTaggedValue BuiltinsBoolean::BooleanPrototypeToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let b be thisBooleanValue(this value). + JSTaggedValue thisValueToBoolean = BuiltinsBoolean::ThisBooleanValue(thread, GetThis(argv).GetTaggedValue()); + // 2. ReturnIfAbrupt(b) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If b is true, return "true"; else return "false". + return thisValueToBoolean.IsTrue() ? GetTaggedString(thread, "true") : GetTaggedString(thread, "false"); +} + +// ecma 19.3.3.3 Boolean.prototype.valueOf () +JSTaggedValue BuiltinsBoolean::BooleanPrototypeValueOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // 1. Return thisBooleanValue(this value). + return BuiltinsBoolean::ThisBooleanValue(argv->GetThread(), GetThis(argv).GetTaggedValue()); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_boolean.h b/ecmascript/builtins/builtins_boolean.h new file mode 100644 index 0000000000000000000000000000000000000000..173190e09c0e4f059003cb0eb8e89216e2a91378 --- /dev/null +++ b/ecmascript/builtins/builtins_boolean.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_BOOLEAN_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_BOOLEAN_H + +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsBoolean : public base::BuiltinsBase { +public: + // ecma 19.3.1 + static JSTaggedValue BooleanConstructor(EcmaRuntimeCallInfo *argv); + + // ecma 19.3.3 abstract operation thisBooleanValue(value) + static JSTaggedValue ThisBooleanValue(JSThread *thread, JSTaggedValue value); + + // ecma 19.3.3.2 + static JSTaggedValue BooleanPrototypeToString(EcmaRuntimeCallInfo *argv); + + // ecma 19.3.3.3 + static JSTaggedValue BooleanPrototypeValueOf(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_BOOLEAN_H diff --git a/ecmascript/builtins/builtins_dataview.cpp b/ecmascript/builtins/builtins_dataview.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc635977bf34781c8f0983cef6697ff852d4b2cc --- /dev/null +++ b/ecmascript/builtins/builtins_dataview.cpp @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/number_helper.h" +#include "ecmascript/builtins/builtins_arraybuffer.h" +#include "ecmascript/builtins/builtins_dataview.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript::builtins { +// 24.2.2.1 +JSTaggedValue BuiltinsDataView::DataViewConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), DataView, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle ctor = GetConstructor(argv); + JSHandle newTarget = GetNewTarget(argv); + // 1. If NewTarget is undefined, throw a TypeError exception. + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "newtarget is undefined", JSTaggedValue::Exception()); + } + JSHandle bufferHandle = GetCallArg(argv, 0); + // 2. If Type(buffer) is not Object, throw a TypeError exception. + if (!bufferHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is not Object", JSTaggedValue::Exception()); + } + // 3. If buffer does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. + if (!bufferHandle->IsArrayBuffer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is not ArrayBuffer", JSTaggedValue::Exception()); + } + JSHandle offsetHandle = GetCallArg(argv, 1); + // 4. Let numberOffset be ToNumber(byteOffset). + JSTaggedNumber offsetNumber = JSTaggedValue::ToNumber(thread, offsetHandle); + // 6. ReturnIfAbrupt(offset). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t offset = base::NumberHelper::DoubleInRangeInt32(offsetNumber.GetNumber()); + // 7. If numberOffset ≠ offset or offset < 0, throw a RangeError exception. + if (offset < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Offset out of range", JSTaggedValue::Exception()); + } + // 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(bufferHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is Detached Buffer", JSTaggedValue::Exception()); + } + // 9. Let bufferByteLength be the value of buffer’s [[ArrayBufferByteLength]] internal slot. + JSHandle arrBufHandle(bufferHandle); + JSTaggedNumber bufLenNum = JSTaggedNumber::FromIntOrDouble(thread, arrBufHandle->GetArrayBufferByteLength()); + int32_t bufByteLen = bufLenNum.ToInt32(); + // 10. If offset > bufferByteLength, throw a RangeError exception. + if (offset > bufByteLen) { + THROW_RANGE_ERROR_AND_RETURN(thread, "offset > bufferByteLength", JSTaggedValue::Exception()); + } + int32_t viewByteLen; + JSHandle byteLenHandle = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + // 11. If byteLength is undefined, then Let viewByteLength be bufferByteLength – offset. + if (byteLenHandle->IsUndefined()) { + viewByteLen = bufByteLen - offset; + } else { + // Let viewByteLength be ToIndex(byteLength). + JSTaggedNumber byteLen = JSTaggedValue::ToIndex(thread, byteLenHandle); + // ReturnIfAbrupt(viewByteLength). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + viewByteLen = byteLen.ToInt32(); + // If offset+viewByteLength > bufferByteLength, throw a RangeError exception. + if (offset + viewByteLen > bufByteLen) { + THROW_RANGE_ERROR_AND_RETURN(thread, "offset + viewByteLen > bufByteLen", JSTaggedValue::Exception()); + } + } + // 13. Let O be OrdinaryCreateFromConstructor OrdinaryCreateFromConstructor(NewTarget, "%DataViewPrototype%", + // «[[DataView]],[[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]]» ). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(ctor), newTarget); + // 14. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle dataView(obj); + // 15. Set O’s [[DataView]] internal slot to true. + dataView->SetDataView(thread, JSTaggedValue::True()); + // 16. Set O’s [[ViewedArrayBuffer]] internal slot to buffer. + dataView->SetViewedArrayBuffer(thread, bufferHandle.GetTaggedValue()); + // 17. Set O’s [[ByteLength]] internal slot to viewByteLength. + dataView->SetByteLength(thread, JSTaggedValue(viewByteLen)); + // 18. Set O’s [[ByteOffset]] internal slot to offset. + dataView->SetByteOffset(thread, JSTaggedValue(offset)); + // 19. Return O. + return JSTaggedValue(dataView.GetTaggedValue()); +} + +// 24.2.4.1 +JSTaggedValue BuiltinsDataView::GetBuffer(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), DataView, GetBuffer); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. f Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. + if (!thisHandle->IsDataView()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "O does not have a [[ViewedArrayBuffer]]", JSTaggedValue::Exception()); + } + JSHandle dataView(thisHandle); + // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = dataView->GetViewedArrayBuffer(); + // 5. Return buffer. + return JSTaggedValue(buffer); +} + +// 24.2.4.2 +JSTaggedValue BuiltinsDataView::GetByteLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), DataView, GetByteLength); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. + if (!thisHandle->IsDataView()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "O does not have a [[ViewedArrayBuffer]]", JSTaggedValue::Exception()); + } + JSHandle dataView(thisHandle); + // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = dataView->GetViewedArrayBuffer(); + // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } + // 6. Let size be the value of O’s [[ByteLength]] internal slot. + JSTaggedValue size = dataView->GetByteLength(); + // 7. Return size. + return JSTaggedValue(size); +} + +// 24.2.4.3 +JSTaggedValue BuiltinsDataView::GetOffset(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), DataView, GetOffset); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. + if (!thisHandle->IsDataView()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "O does not have a [[ViewedArrayBuffer]]", JSTaggedValue::Exception()); + } + JSHandle dataView(thisHandle); + // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = dataView->GetViewedArrayBuffer(); + // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } + // 6. Let offset be the value of O’s [[ByteOffset]] internal slot. + JSTaggedValue offset = dataView->GetByteOffset(); + // 7. Return offset. + return JSTaggedValue(offset); +} + +// 24.2.4.5 +JSTaggedValue BuiltinsDataView::GetFloat32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::FLOAT32); +} + +// 24.2.4.6 +JSTaggedValue BuiltinsDataView::GetFloat64(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::FLOAT64); +} + +// 24.2.4.7 +JSTaggedValue BuiltinsDataView::GetInt8(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::INT8); +} + +// 24.2.4.8 +JSTaggedValue BuiltinsDataView::GetInt16(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::INT16); +} + +// 24.2.4.9 +JSTaggedValue BuiltinsDataView::GetInt32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::INT32); +} + +// 24.2.4.10 +JSTaggedValue BuiltinsDataView::GetUint8(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::UINT8); +} + +// 24.2.4.11 +JSTaggedValue BuiltinsDataView::GetUint16(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::UINT16); +} + +// 24.2.4.12 +JSTaggedValue BuiltinsDataView::GetUint32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::UINT32); +} + +// 24.2.4.13 +JSTaggedValue BuiltinsDataView::SetFloat32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::FLOAT32); +} + +// 24.2.4.14 +JSTaggedValue BuiltinsDataView::SetFloat64(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::FLOAT64); +} + +// 24.2.4.15 +JSTaggedValue BuiltinsDataView::SetInt8(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::INT8); +} + +// 24.2.4.16 +JSTaggedValue BuiltinsDataView::SetInt16(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::INT16); +} + +// 24.2.4.17 +JSTaggedValue BuiltinsDataView::SetInt32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::INT32); +} + +// 24.2.4.18 +JSTaggedValue BuiltinsDataView::SetUint8(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::UINT8); +} + +// 24.2.4.19 +JSTaggedValue BuiltinsDataView::SetUint16(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::UINT16); +} + +// 24.2.4.20 +JSTaggedValue BuiltinsDataView::SetUint32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::UINT32); +} + +// 24.2.1.1 +JSTaggedValue BuiltinsDataView::GetViewValue(JSThread *thread, const JSHandle &view, + const JSHandle &requestIndex, JSTaggedValue littleEndian, + DataViewType type) +{ + BUILTINS_API_TRACE(thread, DataView, GetViewValue); + // 1. If Type(view) is not Object, throw a TypeError exception. + if (!view->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception()); + } + // 2. If view does not have a [[DataView]] internal slot, throw a TypeError exception. + if (!view->IsDataView()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "view is not dataview", JSTaggedValue::Exception()); + } + // 3. Let numberIndex be ToNumber(requestIndex). + JSTaggedNumber numberIndex = JSTaggedValue::ToNumber(thread, requestIndex); + // 5. ReturnIfAbrupt(getIndex). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int64_t index = base::NumberHelper::DoubleInRangeInt32(numberIndex.GetNumber()); + // 6. If numberIndex ≠ getIndex or getIndex < 0, throw a RangeError exception. + if (index < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "getIndex < 0", JSTaggedValue::Exception()); + } + // 7. Let isLittleEndian be ToBoolean(isLittleEndian). + bool isLittleEndian; + if (littleEndian.IsUndefined()) { + isLittleEndian = false; + } else { + isLittleEndian = littleEndian.ToBoolean(); + } + // 8. Let buffer be the value of view’s [[ViewedArrayBuffer]] internal slot. + JSHandle dataView(view); + JSTaggedValue buffer = dataView->GetViewedArrayBuffer(); + // 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } + // 10. Let viewOffset be the value of view’s [[ByteOffset]] internal slot. + JSTaggedNumber offsetNum = JSTaggedNumber::FromIntOrDouble(thread, dataView->GetByteOffset()); + int32_t offset = offsetNum.ToInt32(); + // 11. Let viewSize be the value of view’s [[ByteLength]] internal slot. + JSTaggedNumber viewNum = JSTaggedNumber::FromIntOrDouble(thread, dataView->GetByteLength()); + int32_t size = viewNum.ToInt32(); + // 12. Let elementSize be the Number value of the Element Size value specified in Table 49 for Element Type type. + int32_t elementSize = JSDataView::GetElementSize(type); + // 13. If getIndex +elementSize > viewSize, throw a RangeError exception. + if (index + elementSize > size) { + THROW_RANGE_ERROR_AND_RETURN(thread, "getIndex +elementSize > viewSize", JSTaggedValue::Exception()); + } + // 14. Let bufferIndex be getIndex + viewOffset. + int32_t bufferIndex = index + offset; + // 15. Return GetValueFromBuffer(buffer, bufferIndex, type, isLittleEndian). + return BuiltinsArrayBuffer::GetValueFromBuffer(buffer, bufferIndex, type, isLittleEndian); +} + +// 24.2.1.2 +JSTaggedValue BuiltinsDataView::SetViewValue(JSThread *thread, const JSHandle &view, + const JSHandle &requestIndex, JSTaggedValue littleEndian, + DataViewType type, const JSHandle &value) +{ + // 1. If Type(view) is not Object, throw a TypeError exception. + BUILTINS_API_TRACE(thread, DataView, SetViewValue); + if (!view->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception()); + } + // 2. If view does not have a [[DataView]] internal slot, throw a TypeError exception. + if (!view->IsDataView()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "view is not dataview", JSTaggedValue::Exception()); + } + // 3. Let numberIndex be ToNumber(requestIndex). + JSTaggedNumber numberIndex = JSTaggedValue::ToIndex(thread, requestIndex); + // 5. ReturnIfAbrupt(getIndex). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int64_t index = base::NumberHelper::DoubleInRangeInt32(numberIndex.GetNumber()); + // 6. If numberIndex ≠ getIndex or getIndex < 0, throw a RangeError exception. + if (index < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "getIndex < 0", JSTaggedValue::Exception()); + } + JSTaggedNumber numVal = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. Let isLittleEndian be ToBoolean(isLittleEndian). + bool isLittleEndian; + if (littleEndian.IsUndefined()) { + isLittleEndian = false; + } else { + isLittleEndian = littleEndian.ToBoolean(); + } + // 8. Let buffer be the value of view’s [[ViewedArrayBuffer]] internal slot. + JSHandle dataView(view); + JSTaggedValue buffer = dataView->GetViewedArrayBuffer(); + // 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } + // 10. Let viewOffset be the value of view’s [[ByteOffset]] internal slot. + JSTaggedNumber offsetNum = JSTaggedNumber::FromIntOrDouble(thread, dataView->GetByteOffset()); + int32_t offset = offsetNum.ToInt32(); + // 11. Let viewSize be the value of view’s [[ByteLength]] internal slot. + JSTaggedNumber viewNum = JSTaggedNumber::FromIntOrDouble(thread, dataView->GetByteLength()); + int32_t size = viewNum.ToInt32(); + // 12. Let elementSize be the Number value of the Element Size value specified in Table 49 for Element Type type. + int32_t elementSize = JSDataView::GetElementSize(type); + // 13. If getIndex +elementSize > viewSize, throw a RangeError exception. + if (index + elementSize > size) { + THROW_RANGE_ERROR_AND_RETURN(thread, "getIndex +elementSize > viewSize", JSTaggedValue::Exception()); + } + // 14. Let bufferIndex be getIndex + viewOffset. + int32_t bufferIndex = index + offset; + // 15. Return SetValueFromBuffer(buffer, bufferIndex, type, value, isLittleEndian). + return BuiltinsArrayBuffer::SetValueInBuffer(buffer, bufferIndex, type, numVal, isLittleEndian); +} + +JSTaggedValue BuiltinsDataView::GetTypedValue(EcmaRuntimeCallInfo *argv, DataViewType type) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisHandle = GetThis(argv); + JSHandle offsetHandle = GetCallArg(argv, 0); + if (type == DataViewType::UINT8 || type == DataViewType::INT8) { + return GetViewValue(thread, thisHandle, offsetHandle, JSTaggedValue::True(), type); + } + JSHandle littleEndianHandle = GetCallArg(argv, 1); + return GetViewValue(thread, thisHandle, offsetHandle, littleEndianHandle.GetTaggedValue(), type); +} + +JSTaggedValue BuiltinsDataView::SetTypedValue(EcmaRuntimeCallInfo *argv, DataViewType type) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisHandle = GetThis(argv); + JSHandle offsetHandle = GetCallArg(argv, 0); + JSHandle value = GetCallArg(argv, 1); + if (type == DataViewType::UINT8 || type == DataViewType::INT8) { + return SetViewValue(thread, thisHandle, offsetHandle, JSTaggedValue::True(), type, value); + } + JSHandle littleEndianHandle = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + return SetViewValue(thread, thisHandle, offsetHandle, littleEndianHandle.GetTaggedValue(), type, value); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_dataview.h b/ecmascript/builtins/builtins_dataview.h new file mode 100644 index 0000000000000000000000000000000000000000..7ddc0c8d2db21cdfcabe3742752bcad9427f7c0e --- /dev/null +++ b/ecmascript/builtins/builtins_dataview.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_DATAVIEW_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_DATAVIEW_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_dataview.h" + +namespace panda::ecmascript::builtins { +using DataViewType = ecmascript::DataViewType; +class BuiltinsDataView : public base::BuiltinsBase { +public: + // 24.2.2.1 DataView (buffer [ , byteOffset [ , byteLength ] ] ) + static JSTaggedValue DataViewConstructor(EcmaRuntimeCallInfo *argv); + // 24.2.4.1 get DataView.prototype.buffer + static JSTaggedValue GetBuffer(EcmaRuntimeCallInfo *argv); + // 24.2.4.2 get DataView.prototype.byteLength + static JSTaggedValue GetByteLength(EcmaRuntimeCallInfo *argv); + // 24.2.4.3 get DataView.prototype.byteOffset + static JSTaggedValue GetOffset(EcmaRuntimeCallInfo *argv); + // 24.2.4.5 DataView.prototype.getFloat32 ( byteOffset [ , littleEndian ] ) + static JSTaggedValue GetFloat32(EcmaRuntimeCallInfo *argv); + // 24.2.4.6 DataView.prototype.getFloat64 ( byteOffset [ , littleEndian ] ) + static JSTaggedValue GetFloat64(EcmaRuntimeCallInfo *argv); + // 24.2.4.7 DataView.prototype.getInt8 ( byteOffset ) + static JSTaggedValue GetInt8(EcmaRuntimeCallInfo *argv); + // 24.2.4.8 DataView.prototype.getInt16 ( byteOffset [ , littleEndian ] ) + static JSTaggedValue GetInt16(EcmaRuntimeCallInfo *argv); + // 24.2.4.9 DataView.prototype.getInt32 ( byteOffset [ , littleEndian ] ) + static JSTaggedValue GetInt32(EcmaRuntimeCallInfo *argv); + // 24.2.4.10 DataView.prototype.getUint8 ( byteOffset ) + static JSTaggedValue GetUint8(EcmaRuntimeCallInfo *argv); + // 24.2.4.11 DataView.prototype.getUint16 ( byteOffset [ , littleEndian ] ) + static JSTaggedValue GetUint16(EcmaRuntimeCallInfo *argv); + // 24.2.4.12 DataView.prototype.getUint32 ( byteOffset [ , littleEndian ] ) + static JSTaggedValue GetUint32(EcmaRuntimeCallInfo *argv); + // 24.2.4.13 DataView.prototype.setFloat32 ( byteOffset, value [ , littleEndian ] ) + static JSTaggedValue SetFloat32(EcmaRuntimeCallInfo *argv); + // 24.2.4.14 DataView.prototype.setFloat64 ( byteOffset, value [ , littleEndian ] ) + static JSTaggedValue SetFloat64(EcmaRuntimeCallInfo *argv); + // 24.2.4.15 DataView.prototype.setInt8 ( byteOffset, value ) + static JSTaggedValue SetInt8(EcmaRuntimeCallInfo *argv); + // 24.2.4.16 DataView.prototype.setInt16 ( byteOffset, value [ , littleEndian ] ) + static JSTaggedValue SetInt16(EcmaRuntimeCallInfo *argv); + // 24.2.4.17 DataView.prototype.setInt32 ( byteOffset, value [ , littleEndian ] ) + static JSTaggedValue SetInt32(EcmaRuntimeCallInfo *argv); + // 24.2.4.18 DataView.prototype.setUint8 ( byteOffset, value ) + static JSTaggedValue SetUint8(EcmaRuntimeCallInfo *argv); + // 24.2.4.19 DataView.prototype.setUint16( byteOffset, value [ , littleEndian ] ) + static JSTaggedValue SetUint16(EcmaRuntimeCallInfo *argv); + // 24.2.4.20 DataView.prototype.setUint32 ( byteOffset, value [ , littleEndian ] ) + static JSTaggedValue SetUint32(EcmaRuntimeCallInfo *argv); + +private: + // 24.2.1.1 GetViewValue ( view, requestIndex, isLittleEndian, type ) + static JSTaggedValue GetViewValue(JSThread *thread, const JSHandle &view, + const JSHandle &requestIndex, JSTaggedValue littleEndian, + DataViewType type); + static JSTaggedValue SetViewValue(JSThread *thread, const JSHandle &view, + const JSHandle &requestIndex, JSTaggedValue littleEndian, + DataViewType type, const JSHandle &value); + + static JSTaggedValue GetTypedValue(EcmaRuntimeCallInfo *argv, DataViewType type); + static JSTaggedValue SetTypedValue(EcmaRuntimeCallInfo *argv, DataViewType type); +}; +} // namespace panda::ecmascript::builtins + +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_DATAVIEW_H diff --git a/ecmascript/builtins/builtins_date.cpp b/ecmascript/builtins/builtins_date.cpp new file mode 100644 index 0000000000000000000000000000000000000000..10790de701b43dba7def2c533c115f3e228c597b --- /dev/null +++ b/ecmascript/builtins/builtins_date.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_date.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_date.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/tagged_array.h" + +namespace panda::ecmascript::builtins { +// constructor +JSTaggedValue BuiltinsDate::DateConstructor(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Date, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + double now = JSDate::Now().GetDouble(); + CString str = JSDate::ToDateString(now); + return GetTaggedString(thread, str.c_str()); + } + + JSTaggedValue timeValue(0.0); + uint32_t length = argv->GetArgsNumber(); + if (length == 0) { // no value + timeValue = JSDate::Now(); + } else if (length == 1) { // one value + JSHandle value = GetCallArg(argv, 0); + if (value->IsDate()) { // The value is a date object. + JSHandle jsDate(thread, JSDate::Cast(value->GetTaggedObject())); + timeValue = jsDate->GetTimeValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + JSHandle objValue(thread, JSTaggedValue::ToPrimitive(thread, value)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (objValue->IsString()) { // The value is a string object. + timeValue = JSDate::Parse(argv); + } else { // The value is a number. + JSTaggedNumber val = JSTaggedValue::ToNumber(thread, objValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + timeValue = JSTaggedValue(val.GetNumber()); + } + timeValue = JSTaggedValue(JSDate::TimeClip(timeValue.GetDouble())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } else { // two or more values + std::array fields = {0, 0, 1, 0, 0, 0, 0, 0, 0}; + if (length > CONSTRUCTOR_MAX_LENGTH) { // The max length is 7. + length = CONSTRUCTOR_MAX_LENGTH; + } + uint32_t i = 0; + for (; i < length; ++i) { + JSHandle value = GetCallArg(argv, i); + JSTaggedNumber res = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double temp = res.GetNumber(); + if (std::isnan(temp) || !std::isfinite(temp)) { // Check the double value is finite. + break; + } + fields[i] = static_cast(temp); + if (i == 0 && fields[0] >= 0 && fields[0] < JSDate::HUNDRED) { + fields[0] += JSDate::NINETEEN_HUNDRED_YEAR; + } + } + timeValue = JSTaggedValue((i == length) ? JSDate::SetDateValues(&fields, true) : base::NAN_VALUE); + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle constructor = GetConstructor(argv); + JSHandle dateObject = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + dateObject->SetTimeValue(thread, timeValue); + return JSTaggedValue(JSObject::Cast(static_cast(*dateObject))); +} + +// 20.4.3.1 +JSTaggedValue BuiltinsDate::Now([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Date, Now); + return JSDate::Now(); +} + +// 20.4.3.2 +JSTaggedValue BuiltinsDate::Parse(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Date, Parse); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + return JSDate::Parse(argv); +} + +// 20.4.3.4 +JSTaggedValue BuiltinsDate::UTC(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Date, UTC); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + return JSDate::UTC(argv); +} + +// 20.4.4.10 +JSTaggedValue BuiltinsDate::GetTime(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Date, GetTime); + JSThread *thread = argv->GetThread(); + JSHandle msg = GetThis(argv); + if (!msg->IsDate()) { + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); + } + return JSDate::Cast(msg->GetTaggedObject())->GetTime(); +} + +JSTaggedValue BuiltinsDate::SetTime(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Date, SetTime); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle msg = GetThis(argv); + if (!msg->IsDate()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); + } + JSHandle js_data(thread, JSDate::Cast(msg->GetTaggedObject())); + JSTaggedNumber res = JSTaggedValue::ToNumber(thread, GetCallArg(argv, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + double number = res.GetNumber(); + double value = JSDate::TimeClip(number); + js_data->SetTimeValue(thread, JSTaggedValue(value)); + return GetTaggedDouble(value); +} + +// 20.4.4.37 +JSTaggedValue BuiltinsDate::ToJSON(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Date, ToJSON); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle msg = GetThis(argv); + JSHandle object = JSTaggedValue::ToObject(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2. Let tv be ToPrimitive(hint Number) + JSHandle objectHandle = JSHandle::Cast(object); + JSHandle tv(thread, + JSTaggedValue::ToPrimitive(thread, objectHandle, PreferredPrimitiveType::PREFER_NUMBER)); + + // 3. If Type(tv) is Number and tv is not finite, return null + if (tv->IsNumber()) { + if (tv->IsDouble() && !std::isfinite(tv->GetDouble())) { + return JSTaggedValue::Null(); + } + } + JSHandle calleeKey(thread->GetEcmaVM()->GetFactory()->NewFromString("toISOString")); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + return JSFunction::Invoke(thread, objectHandle, calleeKey, factory->EmptyArray()); +} + +// 20.4.4.44 +JSTaggedValue BuiltinsDate::ValueOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Date, ValueOf); + JSThread *thread = argv->GetThread(); + JSHandle msg = GetThis(argv); + if (!msg->IsDate()) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); + } + return JSDate::Cast(msg->GetTaggedObject())->ValueOf(); +} + +// 20.4.4.45 +JSTaggedValue BuiltinsDate::ToPrimitive(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Date, ToPrimitive); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle object = GetThis(argv); + if (!object->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a JSObject", JSTaggedValue::Exception()); + } + JSHandle hint = GetCallArg(argv, 0); + PreferredPrimitiveType tryFirst = PREFER_STRING; + if (hint->IsString()) { + JSHandle numberStrHandle = factory->NewFromString("number"); + if (EcmaString::StringsAreEqual(hint.GetObject(), *numberStrHandle)) { + tryFirst = PREFER_NUMBER; + } else { + JSHandle stringStrHandle = factory->NewFromString("string"); + JSHandle defaultStrHandle = factory->NewFromString("default"); + if (EcmaString::StringsAreEqual(hint.GetObject(), *stringStrHandle) || + EcmaString::StringsAreEqual(hint.GetObject(), *defaultStrHandle)) { + tryFirst = PREFER_STRING; + } else { + THROW_TYPE_ERROR_AND_RETURN(thread, "This is not a primitiveType.", JSTaggedValue::Exception()); + } + } + } else { + THROW_TYPE_ERROR_AND_RETURN(thread, "This is not an primitiveType.", JSTaggedValue::Exception()); + } + return JSTaggedValue::OrdinaryToPrimitive(thread, object, tryFirst); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_date.h b/ecmascript/builtins/builtins_date.h new file mode 100644 index 0000000000000000000000000000000000000000..13f7c54e01e9516b4d80b930763b6b473b64b559 --- /dev/null +++ b/ecmascript/builtins/builtins_date.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_DATE_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_DATE_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_date.h" + +namespace panda::ecmascript::builtins { +class BuiltinsDate : public base::BuiltinsBase { +public: + // 20.4.2 The Date Constructor + static JSTaggedValue DateConstructor(EcmaRuntimeCallInfo *argv); + + // 20.4.3.1 Date.now ( ) + static JSTaggedValue Now(EcmaRuntimeCallInfo *argv); + + // 20.4.3.4 Date.UTC ( year [ , month [ , date [ , hours [ , minutes [ , seconds [ , ms ] ] ] ] ] ] ) + static JSTaggedValue UTC(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Parse(EcmaRuntimeCallInfo *argv); + + // 20.4.4.2 Date.prototype.getDate ( ) + GET_DATE_VALUE(GetDate, DAYS, true); + + // 20.4.4.3 Date.prototype.getDay ( ) + GET_DATE_VALUE(GetDay, WEEKDAY, true); + + // 20.4.4.4 Date.prototype.getFullYear ( ) + GET_DATE_VALUE(GetFullYear, YEAR, true); + + // 20.4.4.5 Date.prototype.getHours ( ) + GET_DATE_VALUE(GetHours, HOUR, true); + + // 20.4.4.6 Date.prototype.getMilliseconds ( ) + GET_DATE_VALUE(GetMilliseconds, MS, true); + + // 20.4.4.7 Date.prototype.getMinutes ( ) + GET_DATE_VALUE(GetMinutes, MIN, true); + + // 20.4.4.8 Date.prototype.getMonth ( ) + GET_DATE_VALUE(GetMonth, MONTH, true); + + // 20.4.4.9 Date.prototype.getSeconds ( ) + GET_DATE_VALUE(GetSeconds, SEC, true); + + // 20.4.4.10 Date.prototype.getTime ( ) + static JSTaggedValue GetTime(EcmaRuntimeCallInfo *argv); + + // 20.4.4.11 Date.prototype.getTimezoneOffset ( ) + GET_DATE_VALUE(GetTimezoneOffset, TIMEZONE, true); + + // 20.4.4.12 Date.prototype.getUTCDate ( ) + GET_DATE_VALUE(GetUTCDate, DAYS, false); + + // 20.4.4.13 Date.prototype.getUTCDay ( ) + GET_DATE_VALUE(GetUTCDay, WEEKDAY, false); + + // 20.4.4.14 Date.prototype.getUTCFullYear ( ) + GET_DATE_VALUE(GetUTCFullYear, YEAR, false); + + // 20.4.4.15 Date.prototype.getUTCHours ( ) + GET_DATE_VALUE(GetUTCHours, HOUR, false); + + // 20.4.4.16 Date.prototype.getUTCMilliseconds ( ) + GET_DATE_VALUE(GetUTCMilliseconds, MS, false); + + // 20.4.4.17 Date.prototype.getUTCMinutes ( ) + GET_DATE_VALUE(GetUTCMinutes, MIN, false); + + // 20.4.4.18 Date.prototype.getUTCMonth ( ) + GET_DATE_VALUE(GetUTCMonth, MONTH, false); + + // 20.4.4.19 Date.prototype.getUTCSeconds ( ) + GET_DATE_VALUE(GetUTCSeconds, SEC, false); + + // 20.3.4.20 Date.prototype.setDate ( date ) + SET_DATE_VALUE(SetDate, CODE_SET_DATE, true); + + // 20.3.4.21 Date.prototype.setFullYear ( year [ , month [ , date ] ] ) + SET_DATE_VALUE(SetFullYear, CODE_SET_FULL_YEAR, true); + + // 20.3.4.22 Date.prototype.setHours ( hour [ , min [ , sec [ , ms ] ] ] ) + SET_DATE_VALUE(SetHours, CODE_SET_HOURS, true); + + // 20.3.4.23 Date.prototype.setMilliseconds ( ms ) + SET_DATE_VALUE(SetMilliseconds, CODE_SET_MILLISECONDS, true); + + // 20.3.4.24 Date.prototype.setMinutes ( min [ , sec [ , ms ] ] ) + SET_DATE_VALUE(SetMinutes, CODE_SET_MINUTES, true); + + // 20.3.4.25 Date.prototype.setMonth ( month [ , date ] ) + SET_DATE_VALUE(SetMonth, CODE_SET_MONTH, true); + + // 20.3.4.26 Date.prototype.setSeconds ( sec [ , ms ] ) + SET_DATE_VALUE(SetSeconds, CODE_SET_SECONDS, true); + + // 20.3.4.27 Date.prototype.setTime ( time ) + static JSTaggedValue SetTime(EcmaRuntimeCallInfo *argv); + + // 20.3.4.28 Date.prototype.setUTCDate ( date ) + SET_DATE_VALUE(SetUTCDate, CODE_SET_DATE, false); + + // 20.3.4.29 Date.prototype.setUTCFullYear ( year [ , month [ , date ] ] ) + SET_DATE_VALUE(SetUTCFullYear, CODE_SET_FULL_YEAR, false); + + // 20.3.4.30 Date.prototype.setUTCHours ( hour [ , min [ , sec [ , ms ] ] ] ) + SET_DATE_VALUE(SetUTCHours, CODE_SET_HOURS, false); + + // 20.3.4.31 Date.prototype.setUTCMilliseconds ( ms ) + SET_DATE_VALUE(SetUTCMilliseconds, CODE_SET_MILLISECONDS, false); + + // 20.3.4.32 Date.prototype.setUTCMinutes ( min [ , sec [, ms ] ] ) + SET_DATE_VALUE(SetUTCMinutes, CODE_SET_MINUTES, false); + + // 20.3.4.33 Date.prototype.setUTCMonth ( month [ , date ] ) + SET_DATE_VALUE(SetUTCMonth, CODE_SET_MONTH, false); + + // 20.3.4.34 Date.prototype.setUTCSeconds ( sec [ , ms ] ) + SET_DATE_VALUE(SetUTCSeconds, CODE_SET_SECONDS, false); + + // 20.4.4.35 Date.prototype.toDateString ( ) + DATE_STRING(ToDateString); + + // 20.4.4.36 Date.prototype.toISOString ( ) + DATE_TO_STRING(ToISOString); + + // 20.4.4.37 Date.prototype.toJSON ( key ) + static JSTaggedValue ToJSON(EcmaRuntimeCallInfo *argv); + + // 20.4.4.38 Date.prototype.toLocaleDateString ( [ reserved1 [ , reserved2 ] ] ) + static JSTaggedValue ToLocaleDateString(EcmaRuntimeCallInfo *argv); + + // 20.4.4.39 Date.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ) + static JSTaggedValue ToLocaleString(EcmaRuntimeCallInfo *argv); + + // 20.4.4.40 Date.prototype.toLocaleTimeString ( [ reserved1 [ , reserved2 ] ] ) + static JSTaggedValue ToLocaleTimeString(EcmaRuntimeCallInfo *argv); + + // 20.4.4.41 Date.prototype.toString ( ) + DATE_STRING(ToString); + + // 20.4.4.42 Date.prototype.toTimeString ( ) + DATE_STRING(ToTimeString); + + // 20.4.4.43 Date.prototype.toUTCString ( ) + DATE_STRING(ToUTCString); + + // 20.4.4.44 Date.prototype.valueOf ( ) + static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv); + + // 20.4.4.45 Date.prototype [ @@toPrimitive ] + static JSTaggedValue ToPrimitive(EcmaRuntimeCallInfo *argv); + +private: + // definition for set data code. + static constexpr uint32_t CODE_SET_DATE = 0x32; + static constexpr uint32_t CODE_SET_MILLISECONDS = 0x76; + static constexpr uint32_t CODE_SET_SECONDS = 0x75; + static constexpr uint32_t CODE_SET_MINUTES = 0x74; + static constexpr uint32_t CODE_SET_HOURS = 0x73; + static constexpr uint32_t CODE_SET_MONTH = 0x31; + static constexpr uint32_t CODE_SET_FULL_YEAR = 0x30; + static constexpr uint8_t CONSTRUCTOR_MAX_LENGTH = 7; +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_NUBMER_H diff --git a/ecmascript/builtins/builtins_errors.cpp b/ecmascript/builtins/builtins_errors.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9be6f76d93e23c66eff03d261a5951b32496e2be --- /dev/null +++ b/ecmascript/builtins/builtins_errors.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_errors.h" +#include "ecmascript/base/error_helper.h" +#include "ecmascript/js_tagged_value-inl.h" + +namespace panda::ecmascript::builtins { +using ErrorHelper = base::ErrorHelper; +using ErrorType = base::ErrorType; +// Error +JSTaggedValue BuiltinsError::ErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::ERROR); +} + +JSTaggedValue BuiltinsError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::ERROR); +} + +// RangeError +JSTaggedValue BuiltinsRangeError::RangeErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::RANGE_ERROR); +} + +JSTaggedValue BuiltinsRangeError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::RANGE_ERROR); +} + +// ReferenceError +JSTaggedValue BuiltinsReferenceError::ReferenceErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::REFERENCE_ERROR); +} + +JSTaggedValue BuiltinsReferenceError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::REFERENCE_ERROR); +} + +// TypeError +JSTaggedValue BuiltinsTypeError::TypeErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::TYPE_ERROR); +} + +JSTaggedValue BuiltinsTypeError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::TYPE_ERROR); +} + +JSTaggedValue BuiltinsTypeError::ThrowTypeError(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handle_scope(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, "type error", JSTaggedValue::Exception()); +} + +// URIError +JSTaggedValue BuiltinsURIError::URIErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::URI_ERROR); +} + +JSTaggedValue BuiltinsURIError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::URI_ERROR); +} + +// SyntaxError +JSTaggedValue BuiltinsSyntaxError::SyntaxErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::SYNTAX_ERROR); +} + +JSTaggedValue BuiltinsSyntaxError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::SYNTAX_ERROR); +} + +// EvalError +JSTaggedValue BuiltinsEvalError::EvalErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::EVAL_ERROR); +} + +JSTaggedValue BuiltinsEvalError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::EVAL_ERROR); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_errors.h b/ecmascript/builtins/builtins_errors.h new file mode 100644 index 0000000000000000000000000000000000000000..9e05771d6b9c871b8d4057bff8534bfaf4c7d544 --- /dev/null +++ b/ecmascript/builtins/builtins_errors.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ERRORS_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ERRORS_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsError : public base::BuiltinsBase { +public: + // 19.5.1.1 + static JSTaggedValue ErrorConstructor(EcmaRuntimeCallInfo *argv); + // 19.5.2.4 + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); +}; + +// 19.5.5.2 +class BuiltinsRangeError : public base::BuiltinsBase { +public: + static JSTaggedValue RangeErrorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); +}; + +// 19.5.5.3 +class BuiltinsReferenceError : public base::BuiltinsBase { +public: + static JSTaggedValue ReferenceErrorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); +}; + +// 19.5.5.5 +class BuiltinsTypeError : public base::BuiltinsBase { +public: + static JSTaggedValue TypeErrorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ThrowTypeError(EcmaRuntimeCallInfo *argv); +}; + +// 19.5.5.6 +class BuiltinsURIError : public base::BuiltinsBase { +public: + static JSTaggedValue URIErrorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); +}; + +// 19.5.5.4 +class BuiltinsSyntaxError : public base::BuiltinsBase { +public: + static JSTaggedValue SyntaxErrorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); +}; + +// 19.5.5.1 +class BuiltinsEvalError : public base::BuiltinsBase { +public: + static JSTaggedValue EvalErrorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins + +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ERRORS_H diff --git a/ecmascript/builtins/builtins_function.cpp b/ecmascript/builtins/builtins_function.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc4abcda22d84620031f9a59903968b2285fdaf3 --- /dev/null +++ b/ecmascript/builtins/builtins_function.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_function.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript::builtins { +// ecma 19.2.1 Function (p1, p2, ... , pn, body) +JSTaggedValue BuiltinsFunction::FunctionConstructor(EcmaRuntimeCallInfo *argv) +{ + // not support + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, "Not support eval. Forbidden using new Function()/Function().", + JSTaggedValue::Exception()); +} + +// ecma 19.2.3 The Function prototype object is itself a built-in function object. +// When invoked, it accepts any arguments and returns undefined. +JSTaggedValue BuiltinsFunction::FunctionPrototypeInvokeSelf([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue::Undefined(); +} + +// ecma 19.2.3.1 Function.prototype.apply (thisArg, argArray) +JSTaggedValue BuiltinsFunction::FunctionPrototypeApply(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeApply); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. If IsCallable(func) is false, throw a TypeError exception. + if (!GetThis(argv)->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "apply target is not callable", JSTaggedValue::Exception()); + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle func = GetThis(argv); + JSHandle thisArg = GetCallArg(argv, 0); + // 2. If argArray is null or undefined, then + if (GetCallArg(argv, 1)->IsUndefined()) { // null will also be undefined + // a. Return Call(func, thisArg). + JSHandle emptyArray = factory->NewTaggedArray(0); + return JSFunction::Call(thread, func, thisArg, emptyArray); + } + // 3. Let argList be CreateListFromArrayLike(argArray). + JSHandle arrayObj = GetCallArg(argv, 1); + JSHandle argList = JSHandle::Cast(JSObject::CreateListFromArrayLike(thread, arrayObj)); + + // 4. ReturnIfAbrupt(argList). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. Return Call(func, thisArg, argList). + return JSFunction::Call(thread, func, thisArg, argList); +} + +// ecma 19.2.3.2 Function.prototype.bind (thisArg , ...args) +JSTaggedValue BuiltinsFunction::FunctionPrototypeBind(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeBind); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let Target be the this value. + JSHandle target = GetThis(argv); + // 2. If IsCallable(Target) is false, throw a TypeError exception. + if (!target->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "bind target is not callable", JSTaggedValue::Exception()); + } + + JSHandle thisArg = GetCallArg(argv, 0); + array_size_t argsLength = 0; + if (argv->GetArgsNumber() > 1) { + argsLength = argv->GetArgsNumber() - 1; + } + + // 3. Let args be a new (possibly empty) List consisting of all of the argument + // values provided after thisArg in order. + JSHandle argsArray = factory->NewTaggedArray(argsLength); + for (array_size_t index = 0; index < argsLength; ++index) { + argsArray->Set(thread, index, GetCallArg(argv, index + 1)); + } + // 4. Let F be BoundFunctionCreate(Target, thisArg, args). + JSHandle targetFunction = JSHandle::Cast(target); + JSHandle boundFunction = factory->NewJSBoundFunction(targetFunction, thisArg, argsArray); + // 5. ReturnIfAbrupt(F) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. Let targetHasLength be HasOwnProperty(Target, "length"). + auto globalConst = thread->GlobalConstants(); + JSHandle lengthKey = globalConst->GetHandledLengthString(); + bool targetHasLength = + JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(targetFunction), lengthKey); + // 7. ReturnIfAbrupt(targetHasLength). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + double lengthValue = 0.0; + // 8. If targetHasLength is true, then + if (targetHasLength) { + // a. Let targetLen be Get(Target, "length"). + JSHandle targetLen = JSObject::GetProperty(thread, target, lengthKey).GetValue(); + // b. ReturnIfAbrupt(targetLen). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // c. If Type(targetLen) is not Number, let L be 0. + // d. Else, + // i. Let targetLen be ToInteger(targetLen). + // ii. Let L be the larger of 0 and the result of targetLen minus the number of elements of args. + if (targetLen->IsNumber()) { + // argv include thisArg + lengthValue = + std::max(0.0, JSTaggedValue::ToNumber(thread, targetLen).GetNumber() - static_cast(argsLength)); + } + } + // 9. Else let L be 0. + + // 10. Let status be DefinePropertyOrThrow(F, "length", PropertyDescriptor {[[Value]]: L, + // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}). + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(lengthValue)), false, false, true); + [[maybe_unused]] bool status = + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(boundFunction), lengthKey, desc); + // 11. Assert: status is not an abrupt completion. + ASSERT_PRINT(status, "DefinePropertyOrThrow failed"); + + // 12. Let targetName be Get(Target, "name"). + JSHandle nameKey = globalConst->GetHandledNameString(); + JSHandle targetName = JSObject::GetProperty(thread, target, nameKey).GetValue(); + // 13. ReturnIfAbrupt(targetName). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle boundName(factory->NewFromString("bound")); + // 14. If Type(targetName) is not String, let targetName be the empty string. + // 15. Perform SetFunctionName(F, targetName, "bound"). + if (!targetName->IsString()) { + JSHandle emptyString(factory->GetEmptyString()); + status = JSFunction::SetFunctionName(thread, JSHandle(boundFunction), emptyString, boundName); + } else { + status = JSFunction::SetFunctionName(thread, JSHandle(boundFunction), targetName, boundName); + } + // Assert: status is not an abrupt completion. + ASSERT_PRINT(status, "SetFunctionName failed"); + + // 16. Return F. + return boundFunction.GetTaggedValue(); +} + +// ecma 19.2.3.3 Function.prototype.call (thisArg , ...args) +JSTaggedValue BuiltinsFunction::FunctionPrototypeCall(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeCall); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. If IsCallable(func) is false, throw a TypeError exception. + if (!GetThis(argv)->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "call target is not callable", JSTaggedValue::Exception()); + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle func = GetThis(argv); + JSHandle thisArg = GetCallArg(argv, 0); + array_size_t argsLength = 0; + if (argv->GetArgsNumber() > 1) { + argsLength = argv->GetArgsNumber() - 1; + } + // 2. Let argList be an empty List. + // 3. If this method was called with more than one argument then in left to right order, + // starting with the second argument, append each argument as the last element of argList. + JSHandle argsList = factory->NewTaggedArray(argsLength); + for (array_size_t index = 0; index < argsLength; ++index) { + argsList->Set(thread, index, GetCallArg(argv, index + 1)); + } + + // 5. Return Call(func, thisArg, argList). + return JSFunction::Call(thread, func, thisArg, argsList); +} + +// ecma 19.2.3.5 Function.prototype.toString () +JSTaggedValue BuiltinsFunction::FunctionPrototypeToString(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeToString); + // not implement due to that runtime can not get JS Source Code now. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisValue = GetThis(argv); + if (!thisValue->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "function.toString() target is not callable", JSTaggedValue::Exception()); + } + return GetTaggedString(thread, "Not support function.toString() due to Runtime can not obtain Source Code yet."); +} + +// ecma 19.2.3.6 Function.prototype[@@hasInstance] (V) +JSTaggedValue BuiltinsFunction::FunctionPrototypeHasInstance(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeHasInstance); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + // 1. Let F be the this value. + JSHandle thisValue = GetThis(argv); + // 2. Return OrdinaryHasInstance(F, V). + JSHandle arg = GetCallArg(argv, 0); + return JSFunction::OrdinaryHasInstance(argv->GetThread(), thisValue, arg) ? GetTaggedBoolean(true) + : GetTaggedBoolean(false); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_function.h b/ecmascript/builtins/builtins_function.h new file mode 100644 index 0000000000000000000000000000000000000000..e18e90da0b526bd6ecb90c0961d4183dfc5950d2 --- /dev/null +++ b/ecmascript/builtins/builtins_function.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_FUNCTION_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_FUNCTION_H + +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsFunction : public base::BuiltinsBase { +public: + // ecma 19.2.1 Function (p1, p2, ... , pn, body) + static JSTaggedValue FunctionConstructor(EcmaRuntimeCallInfo *argv); + + // ecma 19.2.3 The Function prototype object is itself a built-in function object. + static JSTaggedValue FunctionPrototypeInvokeSelf(EcmaRuntimeCallInfo *argv); + + // ecma 19.2.3.1 Function.prototype.apply (thisArg, argArray) + static JSTaggedValue FunctionPrototypeApply(EcmaRuntimeCallInfo *argv); + + // ecma 19.2.3.2 Function.prototype.bind (thisArg , ...args) + static JSTaggedValue FunctionPrototypeBind(EcmaRuntimeCallInfo *argv); + + // ecma 19.2.3.3 Function.prototype.call (thisArg , ...args) + static JSTaggedValue FunctionPrototypeCall(EcmaRuntimeCallInfo *argv); + + // ecma 19.2.3.5 Function.prototype.toString () + static JSTaggedValue FunctionPrototypeToString(EcmaRuntimeCallInfo *argv); + + // ecma 19.2.3.6 Function.prototype[@@hasInstance] (V) + static JSTaggedValue FunctionPrototypeHasInstance(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_FUNCTION_H diff --git a/ecmascript/builtins/builtins_generator.cpp b/ecmascript/builtins/builtins_generator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..82e5f1297d5a66daf3686ec3e0cebdcb69b94157 --- /dev/null +++ b/ecmascript/builtins/builtins_generator.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "builtins_generator.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_generator_object.h" + +namespace panda::ecmascript::builtins { +// 26.2.1.1 GeneratorFunction(p1, p2, … , pn, body) +JSTaggedValue BuiltinsGenerator::GeneratorFunctionConstructor(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Generator, Constructor); + // not support + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "Not support eval. Forbidden using new GeneratorFunction().", + JSTaggedValue::Exception()); +} + +// 26.4.1.2 Generator.prototype.next(value) +JSTaggedValue BuiltinsGenerator::GeneratorPrototypeNext(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Generator, PrototypeNext); + // 1.Let g be the this value. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetThis(argv); + if (!msg->IsGeneratorObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a generator object.", JSTaggedValue::Exception()); + } + JSHandle generator(thread, JSGeneratorObject::Cast(*JSTaggedValue::ToObject(thread, msg))); + JSHandle value = GetCallArg(argv, 0); + + // 2.Return ? GeneratorResume(g, value). + JSHandle result = JSGeneratorObject::GeneratorResume(thread, generator, value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 26.4.1.3 Generator.prototype.return(value) +JSTaggedValue BuiltinsGenerator::GeneratorPrototypeReturn(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Generator, PrototypeReturn); + // 1.Let g be the this value. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetThis(argv); + if (!msg->IsGeneratorObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a generator object.", JSTaggedValue::Exception()); + } + JSHandle generator(thread, JSGeneratorObject::Cast(*JSTaggedValue::ToObject(thread, msg))); + + // 2.Let C be Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }. + JSHandle value = GetCallArg(argv, 0); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle completionRecord = + factory->NewCompletionRecord(CompletionRecord::RETURN, value); + + // 3.Return ? GeneratorResumeAbrupt(g, C). + JSHandle result = JSGeneratorObject::GeneratorResumeAbrupt(thread, generator, completionRecord); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 26.4.1.4 Generator.prototype.throw(exception) +JSTaggedValue BuiltinsGenerator::GeneratorPrototypeThrow(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Generator, PrototypeThrow); + // 1.Let g be the this value. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetThis(argv); + if (!msg->IsGeneratorObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a generator object.", JSTaggedValue::Exception()); + } + JSHandle generator(thread, JSGeneratorObject::Cast(*JSTaggedValue::ToObject(thread, msg))); + + // 2.Let C be ThrowCompletion(exception). + JSHandle exception = GetCallArg(argv, 0); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle completionRecord = + factory->NewCompletionRecord(CompletionRecord::THROW, exception); + + // 3.Return ? GeneratorResumeAbrupt(g, C). + JSHandle result = JSGeneratorObject::GeneratorResumeAbrupt(thread, generator, completionRecord); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_generator.h b/ecmascript/builtins/builtins_generator.h new file mode 100644 index 0000000000000000000000000000000000000000..1c7b468c6f8f85b97a33c284d72f28b068dcc194 --- /dev/null +++ b/ecmascript/builtins/builtins_generator.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_GENERATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_GENERATOR_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_tagged_value-inl.h" + +namespace panda::ecmascript::builtins { +class BuiltinsGenerator : public base::BuiltinsBase { +public: + // 26.2.1.1 GeneratorFunction(p1, p2, … , pn, body) + static JSTaggedValue GeneratorFunctionConstructor(EcmaRuntimeCallInfo *argv); + + // 26.4.1.2 Generator.prototype.next(value) + static JSTaggedValue GeneratorPrototypeNext(EcmaRuntimeCallInfo *argv); + + // 26.4.1.3 Generator.prototype.return(value) + static JSTaggedValue GeneratorPrototypeReturn(EcmaRuntimeCallInfo *argv); + + // 26.4.1.4 Generator.prototype.throw(exception) + static JSTaggedValue GeneratorPrototypeThrow(EcmaRuntimeCallInfo *argv); + + // 26.4.1.5 Generator.prototype[@@toStringTag] +}; +} // namespace panda::ecmascript::builtins + +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_GENERATOR_H diff --git a/ecmascript/builtins/builtins_global.cpp b/ecmascript/builtins/builtins_global.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8ed7a6d779969732beab86d0665aef4d7ffc1060 --- /dev/null +++ b/ecmascript/builtins/builtins_global.cpp @@ -0,0 +1,558 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_global.h" +#include +#include +#include +#include +#include "ecmascript/base/number_helper.h" +#include "ecmascript/base/string_helper.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/interpreter/slow_runtime_helper.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript::builtins { +using NumberHelper = base::NumberHelper; +using StringHelper = base::StringHelper; + +// 18.2.1 +JSTaggedValue BuiltinsGlobal::NotSupportEval(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, "not support eval()", JSTaggedValue::Exception()); +} + +// 18.2.2 +JSTaggedValue BuiltinsGlobal::IsFinite(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, IsFinite); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle numberInput = GetCallArg(msg, 0); + // 1. Let num be ToNumber(number). + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, numberInput); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. If num is NaN, +Infinite, or -Infinite, return false. + // 4. Otherwise, return true. + if (std::isfinite(number.GetNumber())) { + return GetTaggedBoolean(true); + } + return GetTaggedBoolean(false); +} + +// 18.2.3 +JSTaggedValue BuiltinsGlobal::IsNaN(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, IsNaN); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle numberInput = GetCallArg(msg, 0); + // 1. Let num be ToNumber(number). + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, numberInput); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If num is NaN, return true. + if (std::isnan(number.GetNumber())) { + return GetTaggedBoolean(true); + } + // 4. Otherwise, return false. + return GetTaggedBoolean(false); +} + +bool BuiltinsGlobal::IsUnescapedURI(uint16_t ch) +{ + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) { + return true; + } + return IsInMarkURISet(ch); +} + +bool BuiltinsGlobal::IsInUnescapedURISet(uint16_t ch) +{ + if (ch == '#') { + return true; + } + return IsUnescapedURI(ch) || IsReservedURI(ch); +} + +bool BuiltinsGlobal::IsInReservedURISet(uint16_t ch) +{ + if (ch == '#') { + return true; + } + return IsReservedURI(ch); +} + +bool BuiltinsGlobal::IsReservedURI(uint16_t ch) +{ + std::u16string str(u";/?:@&=+$,"); + std::u16string::size_type index = str.find(ch); + return (index != std::u16string::npos); +} + +bool BuiltinsGlobal::IsInMarkURISet(uint16_t ch) +{ + std::u16string str(u"-_.!~*'()"); + std::u16string::size_type index = str.find(ch); + return (index != std::u16string::npos); +} + +bool BuiltinsGlobal::IsHexDigits(uint16_t ch) +{ + return ('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'F') || ('a' <= ch && ch <= 'f'); +} + +// 18.2.6 +JSTaggedValue BuiltinsGlobal::DecodeURI(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, DecodeURI); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let uriString be ToString(encodedURI). + // 2. ReturnIfAbrupt(uriString). + [[maybe_unused]] JSHandle uriString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let reservedURISet be a String containing one instance of each code unit valid in uriReserved plus "#". + // 4. Return Decode(uriString, reservedURISet). + return Decode(thread, uriString, IsInReservedURISet); +} + +JSTaggedValue BuiltinsGlobal::EncodeURI(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, EncodeURI); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let uriString be ToString(uri). + // 2. ReturnIfAbrupt(uriString). + [[maybe_unused]] JSHandle uriString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let unescapedURISet be a String containing one instance of + // each code unit valid in uriReserved and uriUnescaped plus "#". + // 4. Return Encode(uriString, unescapedURISet). + return Encode(thread, uriString, IsInUnescapedURISet); +} + +JSTaggedValue BuiltinsGlobal::DecodeURIComponent(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, DecodeURIComponent); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let componentString be ToString(encodedURIComponent). + // 2. ReturnIfAbrupt(componentString). + [[maybe_unused]] JSHandle componentString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let reservedURIComponentSet be the empty String. + // 4. Return Decode(componentString, reservedURIComponentSet). + return Decode(thread, componentString, []([[maybe_unused]] uint16_t unused) { return false; }); +} + +JSTaggedValue BuiltinsGlobal::EncodeURIComponent(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, EncodeURIComponent); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let componentString be ToString(uriComponent). + // 2. ReturnIfAbrupt(componentString). + [[maybe_unused]] JSHandle componentString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let unescapedURIComponentSet be a String containing one instance of each code unit valid in uriUnescaped. + // 4. Return Encode(componentString, unescapedURIComponentSet). + return Encode(thread, componentString, IsUnescapedURI); +} + +// Runtime Semantics +JSTaggedValue BuiltinsGlobal::Encode(JSThread *thread, const JSHandle &str, judgURIFunc IsInURISet) +{ + // 1. Let strLen be the number of code units in string. + uint32_t strLen = str->GetLength(); + // 2. Let R be the empty String. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + std::u16string resStr; + + // 3. Let k be 0. + // 4. Repeat + uint32_t k = 0; + while (true) { + // a. If k equals strLen, return R. + if (k == strLen) { + auto *uint16tData = reinterpret_cast(resStr.data()); + int32_t resSize = resStr.size(); + return factory->NewFromUtf16Literal(uint16tData, resSize).GetTaggedValue(); + } + + // b. Let C be the code unit at index k within string. + // c. If C is in unescapedSet, then + // i. Let S be a String containing only the code unit C. + // ii. Let R be a new String value computed by concatenating the previous value of R and S. + // d. Else C is not in unescapedSet, + uint16_t cc = str->At(k); + if (IsInURISet(cc)) { + std::u16string sStr = StringHelper::Utf16ToU16String(&cc, 1); + resStr.append(sStr); + } else { + // i. If the code unit value of C is not less than 0xDC00 and not greater than 0xDFFF, + // throw a URIError exception. + if (cc >= base::utf_helper::DECODE_TRAIL_LOW && cc <= base::utf_helper::DECODE_TRAIL_HIGH) { + THROW_URI_ERROR_AND_RETURN(thread, "EncodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + + // ii. If the code unit value of C is less than 0xD800 or greater than 0xDBFF, then + // 1. Let V be the code unit value of C. + // iii. Else, + // 1. Increase k by 1. + // 2. If k equals strLen, throw a URIError exception. + // 3. Let kChar be the code unit value of the code unit at index k within string. + // 4. If kChar is less than 0xDC00 or greater than 0xDFFF, throw a URIError exception. + // 5. Let V be UTF16Decode(C, kChar). + uint32_t vv; + if (cc < base::utf_helper::DECODE_LEAD_LOW || cc > base::utf_helper::DECODE_LEAD_HIGH) { + vv = cc; + } else { + k++; + if (k == strLen) { + THROW_URI_ERROR_AND_RETURN(thread, "k is invalid", JSTaggedValue::Exception()); + } + uint16_t kc = str->At(k); + if (kc < base::utf_helper::DECODE_TRAIL_LOW || kc > base::utf_helper::DECODE_TRAIL_HIGH) { + THROW_URI_ERROR_AND_RETURN(thread, "EncodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + vv = base::utf_helper::UTF16Decode(cc, kc); + } + + // iv. Let Octets be the array of octets resulting by applying the UTF-8 transformation to V, + // and let L be the array size. + // v. Let j be 0. + // vi. Repeat, while j < L + // 1. Let jOctet be the value at index j within Octets. + // 2. Let S be a String containing three code units "%XY" where XY are two uppercase hexadecimal + // digits encoding the value of jOctet. + // 3. Let R be a new String value computed by concatenating the previous value of R and S. + // 4. Increase j by 1. + std::string oct = StringHelper::Utf32ToString(vv); + std::string hexStr("0123456789ABCDEF"); + + uint32_t length = oct.length(); + std::stringstream tmpStr; + for (uint32_t j = 0; j < length; j++) { + uint8_t joct = oct.at(j); + tmpStr << '%' << hexStr.at((joct >> 4U) & BIT_MASK) // NOLINT + << hexStr.at(joct & BIT_MASK); // 4: means shift right by 4 digits + } + resStr.append(StringHelper::StringToU16string(tmpStr.str())); + } + + // e. Increase k by 1. + k++; + } +} + +uint8_t BuiltinsGlobal::GetValueFromTwoHex(uint16_t front, uint16_t behind) +{ + ASSERT(IsHexDigits(front) && IsHexDigits(behind)); + std::u16string hexString(u"0123456789ABCDEF"); + + size_t idxf = StringHelper::FindFromU16ToUpper(hexString, &front); + size_t idxb = StringHelper::FindFromU16ToUpper(hexString, &behind); + uint8_t res = ((idxf << 4U) | idxb) & BIT_MASK_FF; // NOLINT 4: means shift left by 4 digits + return res; +} + +// Runtime Semantics +JSTaggedValue BuiltinsGlobal::Decode(JSThread *thread, const JSHandle &str, judgURIFunc IsInURISet) +{ + // 1. Let strLen be the number of code units in string. + [[maybe_unused]] uint32_t strLen = str->GetLength(); + // 2. Let R be the empty String. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + std::u16string resStr; + + // 3. Let k be 0. + // 4. Repeat + uint32_t k = 0; + while (true) { + // a. If k equals strLen, return R. + if (k == strLen) { + auto *uint16tData = reinterpret_cast(resStr.data()); + int32_t resSize = resStr.size(); + return factory->NewFromUtf16Literal(uint16tData, resSize).GetTaggedValue(); + } + + // b. Let C be the code unit at index k within string. + // c. If C is not "%", then + // i. Let S be the String containing only the code unit C. + // d. Else C is "%", + // i. Let start be k. + // iv. Let B be the 8-bit value represented by the two hexadecimal digits at index (k + 1) and (k + 2). + // v. Increment k by 2. + // vi. If the most significant bit in B is 0, then + // 1. Let C be the code unit with code unit value B. + // 2. If C is not in reservedSet, then + // a. Let S be the String containing only the code unit C. + // 3. Else C is in reservedSet, + // a. Let S be the substring of string from index start to index k inclusive. + uint16_t cc = str->At(k); + std::u16string sStr; + if (cc != '%') { + if (cc == 0 && strLen == 1) { + JSHandle tmpEcmaString = factory->NewFromUtf16Literal(&cc, 1); + return tmpEcmaString.GetTaggedValue(); + } + sStr = StringHelper::Utf16ToU16String(&cc, 1); + } else { + [[maybe_unused]] uint32_t start = k; + + // ii. If k + 2 is greater than or equal to strLen, throw a URIError exception. + // iii. If the code units at index (k+1) and (k + 2) within string do not represent hexadecimal digits, + // throw a URIError exception. + if ((k + 2) >= strLen) { // 2: means plus 2 + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + if (!(IsHexDigits(str->At(k + 1)) && IsHexDigits(str->At(k + 2)))) { // 2: means plus 2 + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + + uint16_t frontChar = str->At(k + 1); + uint16_t behindChar = str->At(k + 2); // 2: means plus 2 + uint8_t bb = GetValueFromTwoHex(frontChar, behindChar); + k += 2; // 2: means plus 2 + if ((bb & BIT_MASK_ONE) == 0) { + if (!IsInURISet(bb)) { + sStr = StringHelper::Utf8ToU16String(&bb, 1); + if (bb == 0) { + return factory->NewFromUtf16Literal(reinterpret_cast(sStr.data()), 1) + .GetTaggedValue(); + } + } else { + sStr = StringHelper::StringToU16string(StringHelper::SubString(thread, str, start, k - start + 1)); + } + } else { + // vii. Else the most significant bit in B is 1, + // 1. Let n be the smallest nonnegative integer such that (B << n) & 0x80 is equal to 0. + // 3. Let Octets be an array of 8-bit integers of size n. + // 4. Put B into Octets at index 0. + // 6. Let j be 1. + // 7. Repeat, while j < n + // a. Increment k by 1. + // d. Let B be the 8-bit value represented by the two hexadecimal digits at + // index (k + 1) and (k + 2). + // f. Increment k by 2. + // g. Put B into Octets at index j. + // h. Increment j by 1. + // 9. If V < 0x10000, then + // a. Let C be the code unit V. + // b. If C is not in reservedSet, then + // i. Let S be the String containing only the code unit C. + // c. Else C is in reservedSet, + // i. Let S be the substring of string from index start to index k inclusive. + // 10. Else V ≥ 0x10000, + // a. Let L be (((V – 0x10000) & 0x3FF) + 0xDC00). + // b. Let H be ((((V – 0x10000) >> 10) & 0x3FF) + 0xD800). + // c. Let S be the String containing the two code units H and L. + uint32_t n = 0; + while ((((bb << n) & BIT_MASK_ONE) != 0)) { + n++; + if (n > 4) // 4 : 4 means less than 4 + break; + } + // 2. If n equals 1 or n is greater than 4, throw a URIError exception. + if ((n == 1) || (n > 4)) { // 4: means greater than 4 + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + + std::vector oct = {bb}; + + // 5. If k + (3 × (n – 1)) is greater than or equal to strLen, throw a URIError exception. + if (k + (3 * (n - 1)) >= strLen) { // 3: means multiply by 3 + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + uint32_t j = 1; + while (j < n) { + k++; + uint16_t codeUnit = str->At(k); + // b. If the code unit at index k within string is not "%", throw a URIError exception. + // c. If the code units at index (k +1) and (k + 2) within string do not represent hexadecimal + // digits, throw a URIError exception. + if (!(codeUnit == '%')) { + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + if (!(IsHexDigits(str->At(k + 1)) && IsHexDigits(str->At(k + 2)))) { // 2: means plus 2 + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + + uint16_t frontChart = str->At(k + 1); + uint16_t behindChart = str->At(k + 2); // 2: means plus 2 + bb = GetValueFromTwoHex(frontChart, behindChart); + // e. If the two most significant bits in B are not 10, throw a URIError exception. + if (!((bb & BIT_MASK_TWO) == BIT_MASK_ONE)) { + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + + k += 2; // 2: means plus 2 + oct.push_back(bb); + j++; + } + + // 8. Let V be the value obtained by applying the UTF-8 transformation to Octets, that is, + // from an array of octets into a 21-bit value. If Octets does not contain a valid UTF-8 encoding of + // a Unicode code point throw a URIError exception. + if (!base::utf_helper::IsValidUTF8(oct)) { + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + uint32_t vv = StringHelper::Utf8ToU32String(oct); + if (vv < base::utf_helper::DECODE_SECOND_FACTOR) { + if (!IsInURISet(vv)) { + sStr = StringHelper::Utf16ToU16String(reinterpret_cast(&vv), 1); + } else { + sStr = + StringHelper::StringToU16string(StringHelper::SubString(thread, str, start, k - start + 1)); + } + } else { + uint16_t lv = (((vv - base::utf_helper::DECODE_SECOND_FACTOR) & BIT16_MASK) + + base::utf_helper::DECODE_TRAIL_LOW); + uint16_t hv = ((((vv - base::utf_helper::DECODE_SECOND_FACTOR) >> 10U) & BIT16_MASK) + // NOLINT + base::utf_helper::DECODE_LEAD_LOW); // 10: means shift left by 10 digits + sStr = StringHelper::Append(StringHelper::Utf16ToU16String(&hv, 1), + StringHelper::Utf16ToU16String(&lv, 1)); + } + } + } + // e. Let R be a new String value computed by concatenating the previous value of R and S. + // f. Increase k by 1. + resStr.append(sStr); + k++; + } +} + +void BuiltinsGlobal::PrintString([[maybe_unused]] JSThread *thread, EcmaString *string) +{ + if (string == nullptr) { + return; + } + + CString buffer = ConvertToString(string); + std::cout << buffer; +} + +JSTaggedValue BuiltinsGlobal::PrintEntrypoint(EcmaRuntimeCallInfo *msg) +{ + if (msg == nullptr) { + return JSTaggedValue::Undefined(); + } + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + BUILTINS_API_TRACE(thread, Global, PrintEntryPoint); + + uint32_t numArgs = msg->GetArgsNumber(); + for (uint32_t i = 0; i < numArgs; i++) { + JSHandle stringContent = JSTaggedValue::ToString(thread, GetCallArg(msg, i)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + PrintString(thread, *stringContent); + + if (i != numArgs - 1) { + std::cout << " "; + } + } + std::cout << std::endl; + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsGlobal::CallJsBoundFunction(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, CallJsBoundFunction); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // msg contains jsfunc, this, arg1,... + + uint32_t numArgs = msg->GetArgsNumber(); + JSHandle boundFunc(GetConstructor(msg)); + JSHandle thisObj(thread, boundFunc->GetBoundThis()); + + JSHandle newArgs = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(numArgs); + + for (uint32_t i = 0; i < numArgs; i++) { + newArgs->Set(thread, i, GetCallArg(msg, i).GetTaggedValue()); + } + + return SlowRuntimeHelper::CallBoundFunction(thread, boundFunc, thisObj, newArgs); +} + +JSTaggedValue BuiltinsGlobal::CallJsProxy(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, CallJsProxy); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // msg contains js_proxy, this, arg1,... + int32_t numArgs = msg->GetArgsNumber(); + JSHandle proxy(GetConstructor(msg)); + if (!proxy->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Proxy target is not callable", JSTaggedValue::Undefined()); + } + + // Calling proxy directly should transfer 'undefined' as this + JSHandle thisObj(GetThis(msg)); + + JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(numArgs); + + for (int32_t i = 0; i < numArgs; i++) { + array->Set(thread, i, GetCallArg(msg, i).GetTaggedValue()); + } + + return JSProxy::CallInternal(thread, proxy, thisObj, array); +} + +#ifdef PANDA_ECMASCRIPT_ENABLE_RUNTIME_STAT +JSTaggedValue BuiltinsGlobal::StartRuntimeStat(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // start vm runtime stat statistic + thread->GetEcmaVM()->SetRuntimeStatEnable(true); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsGlobal::StopRuntimeStat(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // start vm runtime stat statistic + thread->GetEcmaVM()->SetRuntimeStatEnable(false); + return JSTaggedValue::Undefined(); +} +#endif +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_global.h b/ecmascript/builtins/builtins_global.h new file mode 100644 index 0000000000000000000000000000000000000000..5523f8590aa108af45515dffccede7f406da39a9 --- /dev/null +++ b/ecmascript/builtins/builtins_global.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_GLOBAL_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_GLOBAL_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_thread.h" + +namespace panda::ecmascript::builtins { +static constexpr uint8_t BIT_MASK = 0x0F; +static constexpr uint8_t BIT_MASK_FF = 0xFF; +static constexpr uint16_t BIT16_MASK = 0x3FF; +static constexpr uint8_t BIT_MASK_ONE = 0x80; +static constexpr uint8_t BIT_MASK_TWO = 0xC0; +using judgURIFunc = bool (*)(uint16_t); + +class BuiltinsGlobal : public base::BuiltinsBase { +public: + // 18.2.1 + static JSTaggedValue NotSupportEval(EcmaRuntimeCallInfo *msg); + // 18.2.2 + static JSTaggedValue IsFinite(EcmaRuntimeCallInfo *msg); + // 18.2.3 + static JSTaggedValue IsNaN(EcmaRuntimeCallInfo *msg); + // 18.2.6 + static JSTaggedValue DecodeURI(EcmaRuntimeCallInfo *msg); + static JSTaggedValue EncodeURI(EcmaRuntimeCallInfo *msg); + static JSTaggedValue DecodeURIComponent(EcmaRuntimeCallInfo *msg); + static JSTaggedValue EncodeURIComponent(EcmaRuntimeCallInfo *msg); + + static JSTaggedValue PrintEntrypoint(EcmaRuntimeCallInfo *msg); + static JSTaggedValue CallJsBoundFunction(EcmaRuntimeCallInfo *msg); + static JSTaggedValue CallJsProxy(EcmaRuntimeCallInfo *msg); +#ifdef PANDA_ECMASCRIPT_ENABLE_RUNTIME_STAT + static JSTaggedValue StartRuntimeStat(EcmaRuntimeCallInfo *msg); + static JSTaggedValue StopRuntimeStat(EcmaRuntimeCallInfo *msg); +#endif + +private: + static void PrintString(JSThread *thread, EcmaString *string); + static void PrintValue(int64_t value, int64_t tag); + static JSTaggedValue Encode(JSThread *thread, const JSHandle &str, judgURIFunc IsInURISet); + static JSTaggedValue Decode(JSThread *thread, const JSHandle &str, judgURIFunc IsInURISet); + static bool IsUnescapedURI(uint16_t ch); + static bool IsInUnescapedURISet(uint16_t ch); + static bool IsInReservedURISet(uint16_t ch); + static bool IsReservedURI(uint16_t ch); + static bool IsInMarkURISet(uint16_t ch); + static bool IsHexDigits(uint16_t ch); + static uint8_t GetValueFromTwoHex(uint16_t front, uint16_t behind); +}; +} // namespace panda::ecmascript::builtins + +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ERROR_H diff --git a/ecmascript/builtins/builtins_iterator.cpp b/ecmascript/builtins/builtins_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..47e1b7d84e2b513293c090f052b03466177deae6 --- /dev/null +++ b/ecmascript/builtins/builtins_iterator.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "builtins_iterator.h" +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_iterator.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsIterator::IteratorConstructor([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsIterator::Next([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsIterator::Throw([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsIterator::Return(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Iterator, Return); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle value = GetCallArg(argv, 0); + JSHandle iterResult = JSIterator::CreateIterResultObject(thread, value, true); + return iterResult.GetTaggedValue(); +} + +JSTaggedValue BuiltinsIterator::GetIteratorObj(EcmaRuntimeCallInfo *argv) +{ + return base::BuiltinsBase::GetThis(argv).GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_iterator.h b/ecmascript/builtins/builtins_iterator.h new file mode 100644 index 0000000000000000000000000000000000000000..fa4883ec1f5c333b6ff6aab07e802ca0bcfbcc0a --- /dev/null +++ b/ecmascript/builtins/builtins_iterator.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ITERATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ITERATOR_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsIterator : public base::BuiltinsBase { +public: + static JSTaggedValue IteratorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Throw(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Return(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue GetIteratorObj(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ITERATOR_H diff --git a/ecmascript/builtins/builtins_json.cpp b/ecmascript/builtins/builtins_json.cpp new file mode 100644 index 0000000000000000000000000000000000000000..692da8075e06167434b546e8086976f0d549e2f5 --- /dev/null +++ b/ecmascript/builtins/builtins_json.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_json.h" +#include "ecmascript/base/json_parser.h" +#include "ecmascript/base/json_stringifier.h" +#include "ecmascript/base/number_helper.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript::builtins { +// 24.5.1 +JSTaggedValue BuiltinsJson::Parse(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Json, Parse); + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + array_size_t argc = argv->GetArgsNumber(); + if (argc == 0) { + JSHandle syntaxError = factory->GetJSError(base::ErrorType::SYNTAX_ERROR, "arg is empty"); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), JSTaggedValue::Exception()); + } + + JSHandle msg = GetCallArg(argv, 0); + JSHandle parseString = JSTaggedValue::ToString(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + panda::ecmascript::base::JsonParser parser(thread); + JSHandle result = parser.Parse(*parseString); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSTaggedValue reviver = JSTaggedValue::Undefined(); + if (argc == 2) { // 2: 2 args + reviver = GetCallArg(argv, 1).GetTaggedValue(); + if (reviver.IsCallable()) { + JSHandle callbackfnHandle(thread, reviver); + // Let root be ! OrdinaryObjectCreate(%Object.prototype%). + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle constructor = env->GetObjectFunction(); + JSHandle root = factory->NewJSObjectByConstructor(JSHandle(constructor), constructor); + // Let rootName be the empty String. + JSHandle rootName(factory->GetEmptyString()); + // Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered). + bool success = JSObject::CreateDataProperty(thread, root, rootName, result); + if (success) { + result = parser.InternalizeJsonProperty(root, rootName, callbackfnHandle); + } + } + } + return result.GetTaggedValue(); +} + +// 24.5.2 +JSTaggedValue BuiltinsJson::Stringify(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Json, Parse); + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + array_size_t argc = argv->GetArgsNumber(); + JSTaggedValue value = GetCallArg(argv, 0).GetTaggedValue(); + JSTaggedValue replacer = JSTaggedValue::Undefined(); + JSTaggedValue gap = JSTaggedValue::Undefined(); + + if (argc == 2) { // 2: 2 args + replacer = GetCallArg(argv, 1).GetTaggedValue(); + } else if (argc == 3) { // 3: 3 args + replacer = GetCallArg(argv, 1).GetTaggedValue(); + gap = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD).GetTaggedValue(); + } + + JSHandle handleValue(thread, value); + JSHandle handleReplacer(thread, replacer); + JSHandle handleGap(thread, gap); + panda::ecmascript::base::JsonStringifier stringifier(thread); + JSHandle result = stringifier.Stringify(handleValue, handleReplacer, handleGap); + + return result.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_json.h b/ecmascript/builtins/builtins_json.h new file mode 100644 index 0000000000000000000000000000000000000000..091bc7378c5c0a9a947731da8d8f7c14a17e1e92 --- /dev/null +++ b/ecmascript/builtins/builtins_json.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_JSON_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_JSON_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +using JSTaggedValue = JSTaggedValue; + +class BuiltinsJson : public base::BuiltinsBase { +public: + static JSTaggedValue Parse(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Stringify(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_JSON_H diff --git a/ecmascript/builtins/builtins_map.cpp b/ecmascript/builtins/builtins_map.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aa08d508d596dd40847c0ff5da9e63e9a6f135f7 --- /dev/null +++ b/ecmascript/builtins/builtins_map.cpp @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "builtins_map.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_map.h" +#include "ecmascript/js_map_iterator.h" +#include "ecmascript/linked_hash_table.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsMap::MapConstructor(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1.If NewTarget is undefined, throw a TypeError exception + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + // throw type error + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + // 2.Let Map be OrdinaryCreateFromConstructor(NewTarget, "%MapPrototype%", «‍[[MapData]]» ). + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + // 3.returnIfAbrupt() + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle map = JSHandle::Cast(obj); + + // 4.Set map’s [[MapData]] internal slot to a new empty List. + JSTaggedValue linkedMap = LinkedHashMap::Create(thread); + map->SetLinkedMap(thread, linkedMap); + // add data into set from iterable + // 5.If iterable is not present, let iterable be undefined. + // 6.If iterable is either undefined or null, let iter be undefined. + JSHandle iterable = GetCallArg(argv, 0); + // 8.If iter is undefined, return set + if (iterable->IsUndefined() || iterable->IsNull()) { + return map.GetTaggedValue(); + } + if (!iterable->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "iterable is not object", JSTaggedValue::Exception()); + } + // Let adder be Get(map, "set"). + JSHandle adderKey(factory->NewFromString("set")); + JSHandle adder = JSObject::GetProperty(thread, JSHandle(map), adderKey).GetValue(); + // ReturnIfAbrupt(adder). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue()); + // If IsCallable(adder) is false, throw a TypeError exception + if (!adder->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue()); + } + // Let iter be GetIterator(iterable). + JSHandle iter(JSIterator::GetIterator(thread, iterable)); + // ReturnIfAbrupt(iter). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue()); + JSHandle keyIndex(thread, JSTaggedValue(0)); + JSHandle valueIndex(thread, JSTaggedValue(1)); + JSHandle next = JSIterator::IteratorStep(thread, iter); + JSMutableHandle status(thread, JSTaggedValue::Undefined()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + while (!next->IsFalse()) { + // ReturnIfAbrupt(next). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + // Let nextValue be IteratorValue(next). + JSHandle nextValue(JSIterator::IteratorValue(thread, next)); + // ReturnIfAbrupt(nextValue). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + + // If Type(nextItem) is not Object + if (!nextValue->IsECMAObject()) { + JSHandle typeError = factory->GetJSError(ErrorType::TYPE_ERROR, "nextItem is not Object"); + JSHandle record( + factory->NewCompletionRecord(CompletionRecord::THROW, JSHandle(typeError))); + JSTaggedValue ret = JSIterator::IteratorClose(thread, iter, record).GetTaggedValue(); + if (!thread->HasPendingException()) { + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, typeError.GetTaggedValue(), ret); + }; + return ret; + } + // Let k be Get(nextItem, "0"). + JSHandle key = JSObject::GetProperty(thread, nextValue, keyIndex).GetValue(); + // If k is an abrupt completion, return IteratorClose(iter, k). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, key); + } + JSHandle array(factory->NewTaggedArray(2)); // 2: key and value pair + array->Set(thread, 0, key); + // Let v be Get(nextItem, "1"). + JSHandle value = JSObject::GetProperty(thread, nextValue, valueIndex).GetValue(); + // If v is an abrupt completion, return IteratorClose(iter, v). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, value); + } + array->Set(thread, 1, value); + // Let status be Call(adder, map, «nextValue.[[value]]»). + JSTaggedValue ret = JSFunction::Call(thread, adder, JSHandle(map), array); + + status.Update(ret); + // If status is an abrupt completion, return IteratorClose(iter, status). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, status); + } + // Let next be IteratorStep(iter). + next = JSIterator::IteratorStep(thread, iter); + } + return map.GetTaggedValue(); +} + +JSTaggedValue BuiltinsMap::Set(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Set); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + + JSHandle key = GetCallArg(argv, 0); + JSHandle value = GetCallArg(argv, 1); + + JSHandle map(self); + JSMap::Set(thread, map, key, value); + return map.GetTaggedValue(); +} + +JSTaggedValue BuiltinsMap::Clear(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Clear); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle self = GetThis(argv); + + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + JSHandle map(self); + JSMap::Clear(thread, map); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsMap::Delete(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Delete); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + + JSHandle map(self); + JSHandle key = GetCallArg(argv, 0); + bool flag = JSMap::Delete(thread, map, key); + return GetTaggedBoolean(flag); +} + +JSTaggedValue BuiltinsMap::Has(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Has); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self(GetThis(argv)); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + JSMap *jsMap = JSMap::Cast(*JSTaggedValue::ToObject(thread, self)); + JSHandle key = GetCallArg(argv, 0); + bool flag = jsMap->Has(key.GetTaggedValue()); + return GetTaggedBoolean(flag); +} + +JSTaggedValue BuiltinsMap::Get(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Get); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self(GetThis(argv)); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + JSMap *jsMap = JSMap::Cast(*JSTaggedValue::ToObject(thread, self)); + JSHandle key = GetCallArg(argv, 0); + JSTaggedValue value = jsMap->Get(key.GetTaggedValue()); + return value; +} + +JSTaggedValue BuiltinsMap::ForEach([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + JSHandle map(thread, JSMap::Cast(*JSTaggedValue::ToObject(thread, self))); + + // 4.If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle func(GetCallArg(argv, 0)); + if (!func->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not Callable", JSTaggedValue::Exception()); + } + // 5.If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArg = GetCallArg(argv, 1); + + // composed arguments + int arguementsLength = 3; + JSHandle array(factory->NewTaggedArray(arguementsLength)); + JSHandle iter(factory->NewJSMapIterator(map, IterationKind::KEY_AND_VALUE)); + JSHandle keyIndex(thread, JSTaggedValue(0)); + JSHandle valueIndex(thread, JSTaggedValue(1)); + JSHandle result = JSIterator::IteratorStep(thread, iter); + while (!result->IsFalse()) { + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result.GetTaggedValue()); + JSHandle iterValue(JSIterator::IteratorValue(thread, result)); + JSHandle key = JSObject::GetProperty(thread, iterValue, keyIndex).GetValue(); + JSHandle value = JSObject::GetProperty(thread, iterValue, valueIndex).GetValue(); + array->Set(thread, 0, value); + array->Set(thread, 1, key); + array->Set(thread, 2, map); // 2: the third arg is map + // Let funcResult be Call(callbackfn, T, «e, e, S»). + JSTaggedValue ret = JSFunction::Call(thread, func, thisArg, array); + // returnIfAbrupt + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret); + result = JSIterator::IteratorStep(thread, iter); + } + + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsMap::Species([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return GetThis(argv).GetTaggedValue(); +} + +JSTaggedValue BuiltinsMap::GetSize(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, GetSize); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self(GetThis(argv)); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + JSMap *jsMap = JSMap::Cast(*JSTaggedValue::ToObject(thread, self)); + int count = jsMap->GetSize(); + return JSTaggedValue(count); +} + +JSTaggedValue BuiltinsMap::Entries(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Entries); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSMapIterator::CreateMapIterator(thread, self, IterationKind::KEY_AND_VALUE); + return iter.GetTaggedValue(); +} + +JSTaggedValue BuiltinsMap::Keys(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Keys); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSMapIterator::CreateMapIterator(thread, self, IterationKind::KEY); + return iter.GetTaggedValue(); +} + +JSTaggedValue BuiltinsMap::Values(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Values); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSMapIterator::CreateMapIterator(thread, self, IterationKind::VALUE); + return iter.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_map.h b/ecmascript/builtins/builtins_map.h new file mode 100644 index 0000000000000000000000000000000000000000..48141f4cee1f1e3b417be5c955cdd7c61226397c --- /dev/null +++ b/ecmascript/builtins/builtins_map.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_MAP_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_MAP_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsMap : public base::BuiltinsBase { +public: + // 23.1.1.1 + static JSTaggedValue MapConstructor(EcmaRuntimeCallInfo *argv); + // 23.1.2.2 + static JSTaggedValue Species(EcmaRuntimeCallInfo *argv); + // 23.1.3.1 + static JSTaggedValue Clear(EcmaRuntimeCallInfo *argv); + // 23.1.3.3 + static JSTaggedValue Delete(EcmaRuntimeCallInfo *argv); + // 23.1.3.4 + static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); + // 23.1.3.5 + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + // 23.1.3.6 + static JSTaggedValue Get(EcmaRuntimeCallInfo *argv); + // 23.1.3.7 + static JSTaggedValue Has(EcmaRuntimeCallInfo *argv); + // 23.1.3.8 + static JSTaggedValue Keys(EcmaRuntimeCallInfo *argv); + // 23.1.3.9 + static JSTaggedValue Set(EcmaRuntimeCallInfo *argv); + // 23.1.3.10 + static JSTaggedValue GetSize(EcmaRuntimeCallInfo *argv); + // 23.1.3.11 + static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_MAP_H diff --git a/ecmascript/builtins/builtins_math.cpp b/ecmascript/builtins/builtins_math.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ade9938150e4cf4243d201e38c21ec0b7fc37b2 --- /dev/null +++ b/ecmascript/builtins/builtins_math.cpp @@ -0,0 +1,754 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "builtins_math.h" +#include +#include +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/js_tagged_number.h" +#include "utils/bit_utils.h" + +namespace panda::ecmascript::builtins { +using NumberHelper = base::NumberHelper; + +// 20.2.2.1 +JSTaggedValue BuiltinsMath::Abs(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Abs); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + if (numberValue.IsDouble()) { + // if number_value is double,NaN,Undefine, deal in this case + // if number_value is a String ,which can change to double. e.g."100",deal in this case + return GetTaggedDouble(std::fabs(numberValue.GetDouble())); + } + // if number_value is int,boolean,null, deal in this case + return GetTaggedInt(std::abs(numberValue.GetInt())); +} + +// 20.2.2.2 +JSTaggedValue BuiltinsMath::Acos(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Acos); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // value == -NaN , <-1 or > 1,result is NaN + if (!std::isnan(std::abs(value)) && value <= 1 && value >= -1) { + result = std::acos(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.3 +JSTaggedValue BuiltinsMath::Acosh(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Acosh); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + if (value >= 1) { + result = std::acosh(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.4 +JSTaggedValue BuiltinsMath::Asin(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Asin); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + if (value >= -1 && value <= 1) { + result = std::asin(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.5 +JSTaggedValue BuiltinsMath::Asinh(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Asinh); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // value == -NaN, NaN, result is NaN + if (!std::isnan(std::abs(value))) { + result = std::asinh(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.6 +JSTaggedValue BuiltinsMath::Atan(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Atan); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // value == -NaN, NaN, result is NaN + if (!std::isnan(std::abs(value))) { + result = std::atan(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.7 +JSTaggedValue BuiltinsMath::Atanh(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Atanh); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + if (value >= -1 && value <= 1) { + result = std::atanh(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.8 +JSTaggedValue BuiltinsMath::Atan2(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Atan2); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msgY = GetCallArg(argv, 0); + JSHandle msgX = GetCallArg(argv, 1); + double result = base::NAN_VALUE; + JSTaggedNumber numberValueY = JSTaggedValue::ToNumber(thread, msgY); + JSTaggedNumber numberValueX = JSTaggedValue::ToNumber(thread, msgX); + double valueY = numberValueY.GetNumber(); + double valueX = numberValueX.GetNumber(); + // y = +0 and x > +0, return +0 + // y = -0 and x > +0, return -0 + if (valueY == 0 && valueX > 0) { + result = valueY; + } else if (std::isfinite(valueY) && valueX == std::numeric_limits::infinity()) { + // y < 0 and y is finite and x is POSITIVE_INFINITY,return -0 + // y >= 0 and y is finite and x is POSITIVE_INFINITY,return +0 + result = valueY >= 0 ? 0 : -0.0; + } else if (!std::isnan(std::abs(valueY)) && !std::isnan(std::abs(valueX))) { + // If either x or y is NaN, the result is NaN + result = std::atan2(valueY, valueX); + } + return GetTaggedDouble(result); +} + +// 20.2.2.9 +JSTaggedValue BuiltinsMath::Cbrt(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Cbrt); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // if value == -NaN, NaN, result is NaN + if (!std::isnan(std::abs(value))) { + result = std::cbrt(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.10 +JSTaggedValue BuiltinsMath::Ceil(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Ceil); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // If value is NaN or -NaN, +infinite, -infinite,return value + if (!std::isfinite(value)) { + // if value is -NaN , return NaN, else return value + if (!std::isnan(std::abs(value))) { + result = value; + } + } else { + result = std::ceil(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.11 +JSTaggedValue BuiltinsMath::Clz32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Clz32); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + constexpr int defaultValue = 32; + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + auto tmpValue = std::abs(value); + auto result = numberValue.ToUint32(); + if (!std::isfinite(tmpValue) || tmpValue == 0 || result == 0) { + // If value is NaN or -NaN, +infinite, -infinite, 0,return 32 + return GetTaggedInt(defaultValue); + } + return GetTaggedInt(__builtin_clz(result)); +} + +// 20.2.2.12 +JSTaggedValue BuiltinsMath::Cos(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Cos); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // If value is NaN or -NaN, +infinite, -infinite, result is NaN + if (std::isfinite(std::abs(value))) { + result = std::cos(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.13 +JSTaggedValue BuiltinsMath::Cosh(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Cosh); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // if value is NaN or -NaN, result is NaN + if (!std::isnan(std::abs(value))) { + result = std::cosh(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.14 +JSTaggedValue BuiltinsMath::Exp(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Exp); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // if value is NaN or -NaN, result is NaN + if (!std::isnan(std::abs(value))) { + result = std::exp(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.15 +JSTaggedValue BuiltinsMath::Expm1(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Expm1); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // if value is NaN or -NaN, result is NaN + if (!std::isnan(std::abs(value))) { + result = std::expm1(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.16 +JSTaggedValue BuiltinsMath::Floor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Floor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // If value is NaN or -NaN, +infinite, -infinite, +0, -0, return value + if (!std::isfinite(value) || value == 0) { + // If value is -NaN, return NaN, else return value + if (!std::isnan(std::abs(value))) { + result = value; + } + } else if (value > 0 && value < 1) { + // If x is greater than 0 but less than 1, the result is +0 + result = 0; + } else { + result = std::floor(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.17 +JSTaggedValue BuiltinsMath::Fround(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Fround); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result; + if (std::isnan(std::abs(value))) { + // If result is NaN or -NaN, the result is NaN + result = base::NAN_VALUE; + } else { + result = static_cast(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.18 +JSTaggedValue BuiltinsMath::Hypot(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Hypot); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + double result = 0; + double value = 0; + int argLen = argv->GetArgsNumber(); + auto numberValue = JSTaggedNumber(0); + for (int i = 0; i < argLen; i++) { + JSHandle msg = GetCallArg(argv, i); + numberValue = JSTaggedValue::ToNumber(thread, msg); + value = numberValue.GetNumber(); + result = std::hypot(result, value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.19 +JSTaggedValue BuiltinsMath::Imul(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Imul); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg1 = GetCallArg(argv, 0); + JSHandle msg2 = GetCallArg(argv, 1); + JSTaggedNumber numberValue1 = JSTaggedValue::ToNumber(thread, msg1); + JSTaggedNumber numberValue2 = JSTaggedValue::ToNumber(thread, msg2); + auto value1 = numberValue1.GetNumber(); + auto value2 = numberValue2.GetNumber(); + if (!std::isfinite(value1) || !std::isfinite(value2)) { + // If value is NaN or -NaN, +infinite, -infinite + return GetTaggedInt(0); + } + value1 = numberValue1.ToInt32(); + value2 = numberValue2.ToInt32(); + // purposely ignoring overflow + auto result = static_cast(static_cast(value1) * static_cast(value2)); + return GetTaggedInt(result); +} + +// 20.2.2.20 +JSTaggedValue BuiltinsMath::Log(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Log); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // If value is NaN , -NaN , or < 0,result is NaN + if (!std::isnan(std::abs(value)) && value >= 0) { + result = std::log(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.21 +JSTaggedValue BuiltinsMath::Log1p(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Log1p); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // If value is NaN , -NaN , or < -1,result is NaN + if (!std::isnan(std::abs(value)) && value >= -1) { + result = std::log1p(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.22 +JSTaggedValue BuiltinsMath::Log10(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Log10); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // If value is NaN , -NaN , or < 0,result is NaN + if (!std::isnan(std::abs(value)) && value >= 0) { + result = std::log10(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.23 +JSTaggedValue BuiltinsMath::Log2(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Log2); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // If value is NaN , -NaN , or < 0,result is NaN + if (!std::isnan(std::abs(value)) && value >= 0) { + result = std::log2(value); + } + return GetTaggedDouble(result); +} + +inline bool IsNegZero(double value) +{ + return (value == 0.0 && (bit_cast(value) & base::DOUBLE_SIGN_MASK) == base::DOUBLE_SIGN_MASK); +} + +// 20.2.2.24 +JSTaggedValue BuiltinsMath::Max(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Max); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + int argLen = argv->GetArgsNumber(); + auto numberValue = JSTaggedNumber(-base::POSITIVE_INFINITY); + // If no arguments are given, the result is -inf + auto result = JSTaggedNumber(-base::POSITIVE_INFINITY); + auto tmpMax = -base::POSITIVE_INFINITY; + auto value = -base::POSITIVE_INFINITY; + for (int i = 0; i < argLen; i++) { + JSHandle msg = GetCallArg(argv, i); + numberValue = JSTaggedValue::ToNumber(thread, msg); + value = numberValue.GetNumber(); + if (std::isnan(std::abs(value))) { + // If any value is NaN, or -NaN, the max result is NaN + result = numberValue; + break; + } + if (value > tmpMax) { + result = numberValue; + tmpMax = value; + } else if (value == 0 && tmpMax == 0 && IsNegZero(tmpMax) && !IsNegZero(value)) { + // if tmp_max is -0, value is 0, max is 0 + result = numberValue; + tmpMax = value; + } + } + return result; +} + +// 20.2.2.25 +JSTaggedValue BuiltinsMath::Min(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Min); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + int argLen = argv->GetArgsNumber(); + auto numberValue = JSTaggedNumber(base::POSITIVE_INFINITY); + // If no arguments are given, the result is inf + auto result = JSTaggedNumber(base::POSITIVE_INFINITY); + auto tmpMin = base::POSITIVE_INFINITY; + auto value = base::POSITIVE_INFINITY; + for (int i = 0; i < argLen; i++) { + JSHandle msg = GetCallArg(argv, i); + numberValue = JSTaggedValue::ToNumber(thread, msg); + value = numberValue.GetNumber(); + if (std::isnan(std::abs(value))) { + // If any value is NaN or -NaN, the min result is NaN + result = numberValue; + break; + } + if (value < tmpMin) { + result = numberValue; + tmpMin = value; + } else if (value == 0 && tmpMin == 0 && !IsNegZero(tmpMin) && IsNegZero(value)) { + // if tmp_min is 0, value is -0, min is -0 + result = numberValue; + tmpMin = value; + } + } + return result; +} + +// 20.2.2.26 +JSTaggedValue BuiltinsMath::Pow(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Pow); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msgX = GetCallArg(argv, 0); + JSHandle msgY = GetCallArg(argv, 1); + JSTaggedNumber numberValueX = JSTaggedValue::ToNumber(thread, msgX); + JSTaggedNumber numberValueY = JSTaggedValue::ToNumber(thread, msgY); + double valueX = numberValueX.GetNumber(); + double valueY = numberValueY.GetNumber(); + // If abs(x) is 1 and y is inf or -inf, the result is NaN + if (std::abs(valueX) == 1 && !std::isfinite(valueY)) { + return GetTaggedDouble(base::NAN_VALUE); + } + double result = std::pow(valueX, valueY); + if (std::isnan(std::abs(result))) { + // If result is NaN or -NaN, the result is NaN + result = base::NAN_VALUE; + } + return GetTaggedDouble(result); +} + +// 20.2.2.27 +JSTaggedValue BuiltinsMath::Random([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Random); + std::random_device rd; + std::default_random_engine engine(rd()); + std::uniform_real_distribution dis(0, std::random_device::max() - 1); + // result range [0,1) + double result = dis(engine) / std::random_device::max(); + return GetTaggedDouble(result); +} + +// 20.2.2.28 +JSTaggedValue BuiltinsMath::Round(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Round); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + auto result = base::NAN_VALUE; + const double diff = 0.5; + double absValue = std::abs(value); + if (!std::isfinite(absValue) || absValue == 0) { + // If value is NaN, +infinite, or -infinite, VRegisterTag is DOUBLE + if (!std::isnan(absValue)) { + // If value is NaN or -NaN, the result is default NaN, else is value + result = value; + } + return GetTaggedDouble(result); + } + // If x is less than 0 but greater than or equal to -0.5, the result is -0 + if (value < 0 && value >= -diff) { + return GetTaggedDouble(-0.0); + } + // If x is greater than 0 but less than 0.5, the result is +0 + if (value > 0 && value < diff) { + return GetTaggedInt(0); + } + // For huge integers + result = std::ceil(value); + if (result - value > diff) { + result -= 1; + } + return GetTaggedDouble(result); +} + +// 20.2.2.29 +JSTaggedValue BuiltinsMath::Sign(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Sign); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + if (std::isnan(std::abs(value))) { + return GetTaggedDouble(std::abs(value)); + } + if (value == 0.0) { + return GetTaggedDouble(value); + } + if (value < 0) { + return GetTaggedInt(-1); + } + return GetTaggedInt(1); +} + +// 20.2.2.30 +JSTaggedValue BuiltinsMath::Sin(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Sin); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // If value is NaN or -NaN, the result is NaN + if (std::isfinite(std::abs(value))) { + result = std::sin(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.31 +JSTaggedValue BuiltinsMath::Sinh(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Sinh); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // If value is NaN or -NaN, the result is NaN + if (!std::isnan(std::abs(value))) { + result = std::sinh(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.32 +JSTaggedValue BuiltinsMath::Sqrt(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Sqrt); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // If value is NaN or -NaN, or value < 0, the result is NaN + if (!std::isnan(std::abs(value)) && value >= 0) { + result = std::sqrt(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.33 +JSTaggedValue BuiltinsMath::Tan(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Tan); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + // If value is NaN or -NaN, +infinite, -infinite, result is NaN + if (std::isfinite(value)) { + result = std::tan(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.34 +JSTaggedValue BuiltinsMath::Tanh(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Tanh); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + if (!std::isnan(std::abs(value))) { + result = std::tanh(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.35 +JSTaggedValue BuiltinsMath::Trunc(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Trunc); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = base::NAN_VALUE; + if (!std::isfinite(value)) { + // if value is +infinite, -infinite, NaN, -NaN, VRegisterTag is double + if (!std::isnan(std::abs(value))) { + // if value is +infinite, -infinite, result is value ; + result = value; + } + } else { + result = std::trunc(value); + } + return GetTaggedDouble(result); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_math.h b/ecmascript/builtins/builtins_math.h new file mode 100644 index 0000000000000000000000000000000000000000..4e780b0ce6a9692b8a65339a5e7e40f75892d1d7 --- /dev/null +++ b/ecmascript/builtins/builtins_math.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_MATH_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_MATH_H + +#include "ecmascript/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsMath : public base::BuiltinsBase { +public: + // 20.2.1.1 + static constexpr double E = 2.718281828459045; + // 20.2.1.2 + static constexpr double LN10 = 2.302585092994046; + // 20.2.1.3 + static constexpr double LN2 = 0.6931471805599453; + // 20.2.1.4 + static constexpr double LOG10E = 0.4342944819032518; + // 20.2.1.5 + static constexpr double LOG2E = 1.4426950408889634; + // 20.2.1.6 + static constexpr double PI = 3.141592653589793; + // 20.2.1.7 + static constexpr double SQRT1_2 = 0.7071067811865476; + // 20.2.1.8 + static constexpr double SQRT2 = 1.4142135623730951; + // 20.2.2.1 + static JSTaggedValue Abs(EcmaRuntimeCallInfo *argv); + // 20.2.2.2 + static JSTaggedValue Acos(EcmaRuntimeCallInfo *argv); + // 20.2.2.3 + static JSTaggedValue Acosh(EcmaRuntimeCallInfo *argv); + // 20.2.2.4 + static JSTaggedValue Asin(EcmaRuntimeCallInfo *argv); + // 20.2.2.5 + static JSTaggedValue Asinh(EcmaRuntimeCallInfo *argv); + // 20.2.2.6 + static JSTaggedValue Atan(EcmaRuntimeCallInfo *argv); + // 20.2.2.7 + static JSTaggedValue Atanh(EcmaRuntimeCallInfo *argv); + // 20.2.2.8 + static JSTaggedValue Atan2(EcmaRuntimeCallInfo *argv); + // 20.2.2.9 + static JSTaggedValue Cbrt(EcmaRuntimeCallInfo *argv); + // 20.2.2.10 + static JSTaggedValue Ceil(EcmaRuntimeCallInfo *argv); + // 20.2.2.11 + static JSTaggedValue Clz32(EcmaRuntimeCallInfo *argv); + // 20.2.2.12 + static JSTaggedValue Cos(EcmaRuntimeCallInfo *argv); + // 20.2.2.13 + static JSTaggedValue Cosh(EcmaRuntimeCallInfo *argv); + // 20.2.2.14 + static JSTaggedValue Exp(EcmaRuntimeCallInfo *argv); + // 20.2.2.15 + static JSTaggedValue Expm1(EcmaRuntimeCallInfo *argv); + // 20.2.2.16 + static JSTaggedValue Floor(EcmaRuntimeCallInfo *argv); + // 20.2.2.17 + static JSTaggedValue Fround(EcmaRuntimeCallInfo *argv); + // 20.2.2.18 + static JSTaggedValue Hypot(EcmaRuntimeCallInfo *argv); + // 20.2.2.19 + static JSTaggedValue Imul(EcmaRuntimeCallInfo *argv); + // 20.2.2.20 + static JSTaggedValue Log(EcmaRuntimeCallInfo *argv); + // 20.2.2.21 + static JSTaggedValue Log1p(EcmaRuntimeCallInfo *argv); + // 20.2.2.22 + static JSTaggedValue Log10(EcmaRuntimeCallInfo *argv); + // 20.2.2.23 + static JSTaggedValue Log2(EcmaRuntimeCallInfo *argv); + // 20.2.2.24 + static JSTaggedValue Max(EcmaRuntimeCallInfo *argv); + // 20.2.2.25 + static JSTaggedValue Min(EcmaRuntimeCallInfo *argv); + // 20.2.2.26 + static JSTaggedValue Pow(EcmaRuntimeCallInfo *argv); + // 20.2.2.27 + static JSTaggedValue Random(EcmaRuntimeCallInfo *argv); + // 20.2.2.28 + static JSTaggedValue Round(EcmaRuntimeCallInfo *argv); + // 20.2.2.29 + static JSTaggedValue Sign(EcmaRuntimeCallInfo *argv); + // 20.2.2.30 + static JSTaggedValue Sin(EcmaRuntimeCallInfo *argv); + // 20.2.2.31 + static JSTaggedValue Sinh(EcmaRuntimeCallInfo *argv); + // 20.2.2.32 + static JSTaggedValue Sqrt(EcmaRuntimeCallInfo *argv); + // 20.2.2.33 + static JSTaggedValue Tan(EcmaRuntimeCallInfo *argv); + // 20.2.2.34 + static JSTaggedValue Tanh(EcmaRuntimeCallInfo *argv); + // 20.2.2.35 + static JSTaggedValue Trunc(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_MATH_H diff --git a/ecmascript/builtins/builtins_number.cpp b/ecmascript/builtins/builtins_number.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f8b40bbc4add0e8c595328ee32276b18eade73e --- /dev/null +++ b/ecmascript/builtins/builtins_number.cpp @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/number_helper.h" +#include "ecmascript/builtins/builtins_number.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_hash_table.h" + +namespace panda::ecmascript::builtins { +using NumberHelper = base::NumberHelper; + +JSTaggedValue BuiltinsNumber::NumberConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle newTarget = GetNewTarget(argv); + // 1. If no arguments were passed to this function invocation, let n be +0. + JSTaggedNumber numberValue(0); + if (argv->GetArgsNumber() > 0) { + // 2. Else, let n be ToNumber(value). + JSHandle numberInput = GetCallArg(argv, 0); + numberValue = JSTaggedValue::ToNumber(thread, numberInput); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 4. If NewTarget is undefined, return n. + if (newTarget->IsUndefined()) { + return numberValue; + } + // 5. Let O be OrdinaryCreateFromConstructor(NewTarget, "%NumberPrototype%", «[[NumberData]]» ). + JSHandle constructor = GetConstructor(argv); + JSHandle result = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(constructor), newTarget); + // 6. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. Set the value of O’s [[NumberData]] internal slot to n. + JSPrimitiveRef::Cast(*result)->SetValue(thread, numberValue); + // 8. Return O. + return result.GetTaggedValue(); +} + +// 20.1.2.2 +JSTaggedValue BuiltinsNumber::IsFinite(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, IsFinite); + JSTaggedValue msg = GetCallArg(argv, 0).GetTaggedValue(); + // 1. If Type(number) is not Number, return false + // 2. If number is NaN, +infinite, or -infinite, return false + if (NumberHelper::IsFinite(msg)) { + return GetTaggedBoolean(true); + } + return GetTaggedBoolean(false); +} + +// 20.1.2.3 +JSTaggedValue BuiltinsNumber::IsInteger(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, IsInteger); + JSThread *thread = argv->GetThread(); + JSHandle msg = GetCallArg(argv, 0); + bool result = false; + // 1. If Type(number) is not Number, return false. + // 2. If number is NaN, +infinite, or -infinite, return false + if (NumberHelper::IsFinite(msg.GetTaggedValue())) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + double value = JSTaggedNumber(msg.GetTaggedValue()).GetNumber(); + // 3. Let integer be ToInteger(number). + JSTaggedNumber number = JSTaggedValue::ToInteger(thread, msg); + // 4. If integer is not equal to number, return false. + // 5. Otherwise, return true. + result = (value == number.GetNumber()); + } + return GetTaggedBoolean(result); +} + +// 20.1.2.4 +JSTaggedValue BuiltinsNumber::IsNaN(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, IsNaN); + JSTaggedValue msg = GetCallArg(argv, 0).GetTaggedValue(); + // 1. If Type(number) is not Number, return false. + // 2. If number is NaN, return true. + if (NumberHelper::IsNaN(msg)) { + return GetTaggedBoolean(true); + } + // 3. Otherwise, return false. + return GetTaggedBoolean(false); +} + +// 20.1.2.5 +JSTaggedValue BuiltinsNumber::IsSafeInteger(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, IsSafeInteger); + JSThread *thread = argv->GetThread(); + JSHandle msg = GetCallArg(argv, 0); + bool result = false; + // 1. If Type(number) is not Number, return false. + // 2. If number is NaN, +infinite, or -infinite, return false + if (NumberHelper::IsFinite(msg.GetTaggedValue())) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + double value = JSTaggedNumber(msg.GetTaggedValue()).GetNumber(); + // 3. Let integer be ToInteger(number). + JSTaggedNumber number = JSTaggedValue::ToInteger(thread, msg); + // 4. If integer is not equal to number, return false. + // 5. If abs(integer) ≤ 253−1, return true. + result = (value == number.GetNumber()) && std::abs(value) <= base::MAX_SAFE_INTEGER; + } + return GetTaggedBoolean(result); +} + +// 18.2.4 +// 20.1.2.12 +JSTaggedValue BuiltinsNumber::ParseFloat(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ParseFloat); + JSThread *thread = argv->GetThread(); + JSHandle msg = GetCallArg(argv, 0); + if (msg->IsUndefined()) { + return GetTaggedDouble(base::NAN_VALUE); + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let inputString be ToString(string). + JSHandle numberString = JSTaggedValue::ToString(thread, msg); + // 2. ReturnIfAbrupt(inputString). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + Span str; + if (UNLIKELY(numberString->IsUtf16())) { + size_t len = base::utf_helper::Utf16ToUtf8Size(numberString->GetDataUtf16(), numberString->GetLength()) - 1; + CVector buf(len); + len = base::utf_helper::ConvertRegionUtf16ToUtf8(numberString->GetDataUtf16(), buf.data(), + numberString->GetLength(), len, 0); + str = Span(buf.data(), len); + } else { + str = Span(numberString->GetDataUtf8(), numberString->GetUtf8Length() - 1); + } + // 4. If neither trimmedString nor any prefix of trimmedString satisfies the syntax of a StrDecimalLiteral + // (see 7.1.3.1), return NaN. + if (NumberHelper::IsEmptyString(str.begin(), str.end())) { + return BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + } + double result = NumberHelper::StringToDouble(str.begin(), str.end(), 0, base::IGNORE_TRAILING); + return GetTaggedDouble(result); +} + +// 18.2.5 +// 20.1.2.13 +JSTaggedValue BuiltinsNumber::ParseInt(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ParseInt); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSHandle arg2 = GetCallArg(argv, 1); + int32_t radix = 0; + + if (!arg2->IsUndefined()) { + // 7. Let R = ToInt32(radix). + radix = JSTaggedValue::ToInt32(thread, arg2); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 1. Let inputString be ToString(string). + JSHandle numberString = JSTaggedValue::ToString(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + Span str; + if (UNLIKELY(numberString->IsUtf16())) { + size_t len = base::utf_helper::Utf16ToUtf8Size(numberString->GetDataUtf16(), numberString->GetLength()) - 1; + CVector buf(len); + len = base::utf_helper::ConvertRegionUtf16ToUtf8(numberString->GetDataUtf16(), buf.data(), + numberString->GetLength(), len, 0); + str = Span(buf.data(), len); + } else { + str = Span(numberString->GetDataUtf8(), numberString->GetUtf8Length() - 1); + } + + JSTaggedValue result = NumberHelper::StringToDoubleWithRadix(str.begin(), str.end(), radix); + return result; +} + +// prototype +// 20.1.3.2 +JSTaggedValue BuiltinsNumber::ToExponential(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ToExponential); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let x be thisNumberValue(this value). + JSTaggedNumber value = ThisNumberValue(argv); + // 2. ReturnIfAbrupt(x). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let f be ToInteger(fractionDigits). + JSHandle digits = GetCallArg(argv, 0); + JSTaggedNumber digitInt = JSTaggedValue::ToInteger(thread, digits); + // 5. ReturnIfAbrupt(f). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + double values = value.GetNumber(); + // 6. If x is NaN, return the String "NaN". + if (std::isnan(values)) { + return GetTaggedString(thread, "NaN"); + } + // 8. If x < 0, then + // a. Let s be "-". + // b. Let x = –x. + // 9. If x = +infinity, then + // a. Return the concatenation of the Strings s and "Infinity". + if (!std::isfinite(values)) { + if (values < 0) { + return GetTaggedString(thread, "-Infinity"); + } + return GetTaggedString(thread, "Infinity"); + } + + // 4. Assert: f is 0, when fractionDigits is undefined. + // 10. If f < 0 or f > 20, throw a RangeError exception + double fraction = digitInt.GetNumber(); + if (digits->IsUndefined()) { + fraction = -1; + } else { + if (fraction < base::MIN_FRACTION || fraction > base::MAX_FRACTION) { + THROW_RANGE_ERROR_AND_RETURN(thread, "fraction must be 0 to 100", JSTaggedValue::Exception()); + } + } + return NumberHelper::DoubleToExponential(thread, values, static_cast(fraction)); +} + +// 20.1.3.3 +JSTaggedValue BuiltinsNumber::ToFixed(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ToFixed); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let x be thisNumberValue(this value). + JSTaggedNumber value = ThisNumberValue(argv); + // 2. ReturnIfAbrupt(x). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Let f be ToInteger(fractionDigits). (If fractionDigits is undefined, this step produces the value 0). + JSHandle digitArgv = GetCallArg(argv, 0); + JSTaggedNumber digitInt = JSTaggedValue::ToInteger(thread, digitArgv); + if (digitArgv->IsUndefined()) { + digitInt = JSTaggedNumber(0); + } + // 4. ReturnIfAbrupt(f). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + double digit = digitInt.GetNumber(); + if (digit < base::MIN_FRACTION || digit > base::MAX_FRACTION) { + THROW_RANGE_ERROR_AND_RETURN(thread, "fraction must be 0 to 100", JSTaggedValue::Exception()); + } + + // 6. If x is NaN, return the String "NaN". + double valueNumber = value.GetNumber(); + if (std::isnan(valueNumber)) { + return GetTaggedString(thread, "NaN"); + } + // 9. If x  1021, then + // a. Let m = ToString(x). + const double FIRST_NO_FIXED = 1e21; + if (valueNumber >= FIRST_NO_FIXED) { + return value.ToString(thread).GetTaggedValue(); + } + + return NumberHelper::DoubleToFixed(thread, valueNumber, static_cast(digit)); +} + +// 20.1.3.5 +JSTaggedValue BuiltinsNumber::ToPrecision(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ToPrecision); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let x be thisNumberValue(this value). + JSTaggedNumber value = ThisNumberValue(argv); + // 2. ReturnIfAbrupt(x). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If precision is undefined, return ToString(x). + JSHandle digitArgv = GetCallArg(argv, 0); + if (digitArgv->IsUndefined()) { + return value.ToString(thread).GetTaggedValue(); + } + // 4. Let p be ToInteger(precision). + JSTaggedNumber digitInt = JSTaggedValue::ToInteger(thread, digitArgv); + // 5. ReturnIfAbrupt(p). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. If x is NaN, return the String "NaN". + double valueNumber = value.GetNumber(); + if (std::isnan(valueNumber)) { + return GetTaggedString(thread, "NaN"); + } + // 9. If x = +infinity, then + // a. Return the String that is the concatenation of s and "Infinity". + if (!std::isfinite(valueNumber)) { + if (valueNumber < 0) { + return GetTaggedString(thread, "-Infinity"); + } + return GetTaggedString(thread, "Infinity"); + } + + // If p < 1 or p > 21, throw a RangeError exception + double digit = digitInt.GetNumber(); + if (digit < base::MIN_FRACTION + 1 || digit > base::MAX_FRACTION) { + THROW_RANGE_ERROR_AND_RETURN(thread, "fraction must be 1 to 100", JSTaggedValue::Exception()); + } + return NumberHelper::DoubleToPrecision(thread, valueNumber, static_cast(digit)); +} + +// 20.1.3.6 +JSTaggedValue BuiltinsNumber::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let x be thisNumberValue(this value). + JSTaggedNumber value = ThisNumberValue(argv); + // 2. ReturnIfAbrupt(x). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If radix is not present, let radixNumber be 10. + // 4. Else if radix is undefined, let radixNumber be 10. + double radix = base::DECIMAL; + JSHandle radixValue = GetCallArg(argv, 0); + // 5. Else let radixNumber be ToInteger(radix). + if (!radixValue->IsUndefined()) { + JSTaggedNumber radixNumber = JSTaggedValue::ToInteger(thread, radixValue); + // 6. ReturnIfAbrupt(x). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + radix = radixNumber.GetNumber(); + } + + // 7. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception. + if (radix < base::MIN_RADIX || radix > base::MAX_RADIX) { + THROW_RANGE_ERROR_AND_RETURN(thread, "radix must be 2 to 36", JSTaggedValue::Exception()); + } + // 8. If radixNumber = 10, return ToString(x). + if (radix == base::DECIMAL) { + return value.ToString(thread).GetTaggedValue(); + } + + double valueNumber = value.GetNumber(); + // If x is NaN, return the String "NaN". + if (std::isnan(valueNumber)) { + return GetTaggedString(thread, "NaN"); + } + // If x = +infinity, then + // Return the String that is the concatenation of s and "Infinity". + if (!std::isfinite(valueNumber)) { + if (valueNumber < 0) { + return GetTaggedString(thread, "-Infinity"); + } + return GetTaggedString(thread, "Infinity"); + } + return NumberHelper::DoubleToString(thread, valueNumber, static_cast(radix)); +} + +// 20.1.3.7 +JSTaggedValue BuiltinsNumber::ValueOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ValueOf); + // 1. Let x be thisNumberValue(this value). + return ThisNumberValue(argv); +} + +JSTaggedNumber BuiltinsNumber::ThisNumberValue(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Number, ThisNumberValue); + JSHandle value = GetThis(argv); + if (value->IsNumber()) { + return JSTaggedNumber(value.GetTaggedValue()); + } + if (value->IsJSPrimitiveRef()) { + JSTaggedValue primitive = JSPrimitiveRef::Cast(value->GetTaggedObject())->GetValue(); + if (primitive.IsNumber()) { + return JSTaggedNumber(primitive); + } + } + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, "not number type", JSTaggedNumber::Exception()); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_number.h b/ecmascript/builtins/builtins_number.h new file mode 100644 index 0000000000000000000000000000000000000000..2b24c220abf5a511ffe2fb299975e66aacb0637b --- /dev/null +++ b/ecmascript/builtins/builtins_number.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_NUMBER_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_NUMBER_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript::builtins { +class BuiltinsNumber : public base::BuiltinsBase { +public: + // 20.1.1.1 + static JSTaggedValue NumberConstructor(EcmaRuntimeCallInfo *argv); + + // 20.1.2.2 + static JSTaggedValue IsFinite(EcmaRuntimeCallInfo *argv); + // 20.1.2.3 + static JSTaggedValue IsInteger(EcmaRuntimeCallInfo *argv); + // 20.1.2.4 + static JSTaggedValue IsNaN(EcmaRuntimeCallInfo *argv); + // 20.1.2.5 + static JSTaggedValue IsSafeInteger(EcmaRuntimeCallInfo *argv); + // 20.1.2.12 + static JSTaggedValue ParseFloat(EcmaRuntimeCallInfo *argv); + // 20.1.2.13 + static JSTaggedValue ParseInt(EcmaRuntimeCallInfo *argv); + + // prototype + // 20.1.3.2 + static JSTaggedValue ToExponential(EcmaRuntimeCallInfo *argv); + // 20.1.3.3 + static JSTaggedValue ToFixed(EcmaRuntimeCallInfo *argv); + // 20.1.3.4 + static JSTaggedValue ToLocaleString(EcmaRuntimeCallInfo *argv); + // 20.1.3.5 + static JSTaggedValue ToPrecision(EcmaRuntimeCallInfo *argv); + // 20.1.3.6 + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 20.1.3.7 + static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv); + +private: + static JSTaggedNumber ThisNumberValue(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_NUBMER_H diff --git a/ecmascript/builtins/builtins_object.cpp b/ecmascript/builtins/builtins_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c967b68fbd76edc7e3233d847b5d30f6290b22a5 --- /dev/null +++ b/ecmascript/builtins/builtins_object.cpp @@ -0,0 +1,932 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_object.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/global_env.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_realm.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript::builtins { +// 19.1.1.1Object ( [ value ] ) +JSTaggedValue BuiltinsObject::ObjectConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + + // 1.If NewTarget is neither undefined nor the active function, then + // a.Return OrdinaryCreateFromConstructor(NewTarget, "%ObjectPrototype%"). + JSHandle constructor = GetConstructor(argv); + JSHandle newTarget = GetNewTarget(argv); + if (!newTarget->IsUndefined() && !(newTarget.GetTaggedValue() == constructor.GetTaggedValue())) { + JSHandle obj = + ecmaVm->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + return obj.GetTaggedValue(); + } + + // 2.If value is null, undefined or not supplied, return ObjectCreate(%ObjectPrototype%). + JSHandle value = GetCallArg(argv, 0); + if (value->IsNull() || value->IsUndefined()) { + JSHandle obj = ecmaVm->GetFactory()->OrdinaryNewJSObjectCreate(env->GetObjectFunctionPrototype()); + return obj.GetTaggedValue(); + } + + // 3.Return ToObject(value). + return JSTaggedValue::ToObject(thread, value).GetTaggedValue(); +} + +// 19.1.2.1Object.assign ( target, ...sources ) +JSTaggedValue BuiltinsObject::Assign(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Assign); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + uint32_t numArgs = argv->GetArgsNumber(); + // 1.Let to be ToObject(target). + JSHandle target = GetCallArg(argv, 0); + JSHandle toAssign = JSTaggedValue::ToObject(thread, target); + // 2.ReturnIfAbrupt(to). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3.If only one argument was passed, return to. + // 4.Let sources be the List of argument values starting with the second argument. + // 5.For each element nextSource of sources, in ascending index order + // a.If nextSource is undefined or null, let keys be an empty List. + // b.Else, + // i.Let from be ToObject(nextSource). + // ii.Let keys be from.[[OwnPropertyKeys]](). + // iii.ReturnIfAbrupt(keys). + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + for (array_size_t i = 1; i < numArgs; i++) { + JSHandle source = GetCallArg(argv, i); + if (!source->IsNull() && !source->IsUndefined()) { + JSHandle from = JSTaggedValue::ToObject(thread, source); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle keys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle::Cast(from)); + // ReturnIfAbrupt(keys) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // c.Repeat for each element nextKey of keys in List order, + // i.Let desc be from.[[GetOwnProperty]](nextKey). + // ii.ReturnIfAbrupt(desc). + // iii.if desc is not undefined and desc.[[Enumerable]] is true, then + // 1.Let propValue be Get(from, nextKey). + // 2.ReturnIfAbrupt(propValue). + // 3.Let status be Set(to, nextKey, propValue, true). + // 4.ReturnIfAbrupt(status). + array_size_t keys_len = keys->GetLength(); + for (array_size_t j = 0; j < keys_len; j++) { + PropertyDescriptor desc(thread); + key.Update(keys->Get(j)); + bool success = JSTaggedValue::GetOwnProperty(thread, JSHandle::Cast(from), key, desc); + // ReturnIfAbrupt(desc) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (success && desc.IsEnumerable()) { + JSTaggedValue value = + FastRuntimeStub::FastGetPropertyByValue(thread, from.GetTaggedValue(), key.GetTaggedValue()); + // ReturnIfAbrupt(prop_value) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + FastRuntimeStub::FastSetPropertyByValue(thread, toAssign.GetTaggedValue(), key.GetTaggedValue(), + value); + // ReturnIfAbrupt(status) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + } + } + + // 6.Return to. + return toAssign.GetTaggedValue(); +} + +// Runtime Semantics +JSTaggedValue BuiltinsObject::ObjectDefineProperties(JSThread *thread, const JSHandle &obj, + const JSHandle &prop) +{ + BUILTINS_API_TRACE(thread, Object, DefineProperties); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1.If Type(O) is not Object, throw a TypeError exception. + if (!obj->IsECMAObject()) { + // throw a TypeError exception + THROW_TYPE_ERROR_AND_RETURN(thread, "is not an object", JSTaggedValue::Exception()); + } + + // 2.Let props be ToObject(Properties). + JSHandle props = JSTaggedValue::ToObject(thread, prop); + + // 3.ReturnIfAbrupt(props). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4.Let keys be props.[[OwnPropertyKeys]](). + JSHandle handleKeys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle::Cast(props)); + + // 5.ReturnIfAbrupt(keys). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6.Let descriptors be an empty List. + // new an empty array and append + array_size_t length = handleKeys->GetLength(); + [[maybe_unused]] JSHandle descriptors = + factory->NewTaggedArray(2 * length); // 2: 2 means two element list + + // 7.Repeat for each element nextKey of keys in List order, + // a.Let propDesc be props.[[GetOwnProperty]](nextKey). + // b.ReturnIfAbrupt(propDesc). + // c.If propDesc is not undefined and propDesc.[[Enumerable]] is true, then + // i.Let descObj be Get( props, nextKey). + // ii.ReturnIfAbrupt(descObj). + // iii.Let desc be ToPropertyDescriptor(descObj). + // iv.ReturnIfAbrupt(desc). + // v.Append the pair (a two element List) consisting of nextKey and desc to the end of descriptors. + for (array_size_t i = 0; i < length; i++) { + PropertyDescriptor propDesc(thread); + + JSHandle handleKey(thread, handleKeys->Get(i)); + + bool success = JSTaggedValue::GetOwnProperty(thread, JSHandle::Cast(props), handleKey, propDesc); + // ReturnIfAbrupt(propDesc) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (success && propDesc.IsEnumerable()) { + JSHandle descObj = + JSTaggedValue::GetProperty(thread, JSHandle::Cast(props), handleKey).GetValue(); + // ReturnIfAbrupt(descObj) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + PropertyDescriptor desc(thread); + JSObject::ToPropertyDescriptor(thread, descObj, desc); + + // ReturnIfAbrupt(desc) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 8.For each pair from descriptors in list order, + // a.Let P be the first element of pair. + // b.Let desc be the second element of pair. + // c.Let status be DefinePropertyOrThrow(O,P, desc). + // d.ReturnIfAbrupt(status). + [[maybe_unused]] bool setSuccess = JSTaggedValue::DefinePropertyOrThrow(thread, obj, handleKey, desc); + + // ReturnIfAbrupt(status) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + + // 9.Return O. + return obj.GetTaggedValue(); +} + +// 19.1.2.2Object.create ( O [ , Properties ] ) +JSTaggedValue BuiltinsObject::Create(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Create); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.If Type(O) is neither Object nor Null, throw a TypeError exception. + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsECMAObject() && !obj->IsNull()) { + // throw a TypeError exception + THROW_TYPE_ERROR_AND_RETURN(thread, "Create: O is neither Object nor Null", JSTaggedValue::Exception()); + } + + JSHandle properties = GetCallArg(argv, 1); + + // 2.Let obj be ObjectCreate(O). + JSHandle objCreate = thread->GetEcmaVM()->GetFactory()->OrdinaryNewJSObjectCreate(obj); + + // 3.If the argument Properties is present and not undefined, then + // a.Return ObjectDefineProperties(obj, Properties). + if (!properties->IsUndefined()) { + return ObjectDefineProperties(thread, JSHandle::Cast(objCreate), properties); + } + + // 4.Return obj. + return objCreate.GetTaggedValue(); +} + +// 19.1.2.3Object.defineProperties ( O, Properties ) +JSTaggedValue BuiltinsObject::DefineProperties(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, DefineProperties); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.Return ObjectDefineProperties(O, Properties). + return ObjectDefineProperties(thread, GetCallArg(argv, 0), GetCallArg(argv, 1)); +} + +// 19.1.2.4Object.defineProperty ( O, P, Attributes ) +JSTaggedValue BuiltinsObject::DefineProperty(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, DefineProperty); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1.If Type(O) is not Object, throw a TypeError exception. + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsECMAObject()) { + // throw a TypeError + THROW_TYPE_ERROR_AND_RETURN(thread, "DefineProperty: O is not Object", JSTaggedValue::Exception()); + } + + // 2.Let key be ToPropertyKey(P). + JSHandle prop = GetCallArg(argv, 1); + JSHandle key = JSTaggedValue::ToPropertyKey(thread, prop); + + // 3.ReturnIfAbrupt(key). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 4.Let desc be ToPropertyDescriptor(Attributes). + PropertyDescriptor desc(thread); + JSObject::ToPropertyDescriptor(thread, GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD), desc); + + // 5.ReturnIfAbrupt(desc). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6.Let success be DefinePropertyOrThrow(O,key, desc). + [[maybe_unused]] bool success = JSTaggedValue::DefinePropertyOrThrow(thread, obj, key, desc); + + // 7.ReturnIfAbrupt(success). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 8.Return O. + return obj.GetTaggedValue(); +} + +// 19.1.2.5Object.freeze ( O ) +JSTaggedValue BuiltinsObject::Freeze(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Freeze); + + // 1.If Type(O) is not Object, return O. + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsECMAObject()) { + return obj.GetTaggedValue(); + } + + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 2.Let status be SetIntegrityLevel( O, "frozen"). + bool status = JSObject::SetIntegrityLevel(thread, JSHandle(obj), IntegrityLevel::FROZEN); + + // 3.ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4.If status is false, throw a TypeError exception. + if (!status) { + // throw a TypeError exception + THROW_TYPE_ERROR_AND_RETURN(thread, "Freeze: freeze failed", JSTaggedValue::Exception()); + } + + // 5.Return O. + return obj.GetTaggedValue(); +} + +// 19.1.2.6 Object.getOwnPropertyDescriptor ( O, P ) +JSTaggedValue BuiltinsObject::GetOwnPropertyDesciptor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, GetOwnPropertyDesciptor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1.Let obj be ToObject(O). + JSHandle func = GetCallArg(argv, 0); + JSHandle handle = JSTaggedValue::ToObject(thread, func); + + // 2.ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3.Let key be ToPropertyKey(P). + JSHandle prop = GetCallArg(argv, 1); + JSHandle key = JSTaggedValue::ToPropertyKey(thread, prop); + + // 4.ReturnIfAbrupt(key). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5.Let desc be obj.[[GetOwnProperty]](key). + PropertyDescriptor desc(thread); + JSTaggedValue::GetOwnProperty(thread, JSHandle::Cast(handle), key, desc); + + // 6.ReturnIfAbrupt(desc). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7.Return FromPropertyDescriptor(desc). + JSHandle res = JSObject::FromPropertyDescriptor(thread, desc); + return res.GetTaggedValue(); +} + +// Runtime Semantics +JSTaggedValue BuiltinsObject::GetOwnPropertyKeys(JSThread *thread, const JSHandle &object, + const KeyType &type) +{ + BUILTINS_API_TRACE(thread, Object, GetOwnPropertyKeys); + // 1.Let obj be ToObject(O). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = JSTaggedValue::ToObject(thread, object); + + // 2.ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3.Let keys be obj.[[OwnPropertyKeys]](). + JSHandle handleKeys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle::Cast(obj)); + + // 4.ReturnIfAbrupt(keys). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5.Let nameList be a new empty List. + // new an empty array and append + array_size_t length = handleKeys->GetLength(); + JSHandle nameList = factory->NewTaggedArray(length); + + // 6.Repeat for each element nextKey of keys in List order, + array_size_t copyLength = 0; + switch (type) { + case KeyType::STRING_TYPE: { + for (array_size_t i = 0; i < length; i++) { + JSTaggedValue key = handleKeys->Get(i); + if (key.IsString()) { + nameList->Set(thread, copyLength, key); + copyLength++; + } + } + break; + } + case KeyType::SYMBOL_TYPE: { + for (array_size_t i = 0; i < length; i++) { + JSTaggedValue key = handleKeys->Get(i); + if (key.IsSymbol()) { + nameList->Set(thread, copyLength, key); + copyLength++; + } + } + break; + } + default: + break; + } + + // 7.Return CreateArrayFromList(nameList). + JSHandle resultList = factory->CopyArray(nameList, length, copyLength); + JSHandle resultArray = JSArray::CreateArrayFromList(thread, resultList); + return resultArray.GetTaggedValue(); +} + +// 19.1.2.7 Object.getOwnPropertyNames ( O ) +JSTaggedValue BuiltinsObject::GetOwnPropertyNames(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, GetOwnPropertyNames); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + JSHandle obj = GetCallArg(argv, 0); + KeyType type = KeyType::STRING_TYPE; + + // 1.Return GetOwnPropertyKeys(O, String). + return GetOwnPropertyKeys(argv->GetThread(), obj, type); +} + +// 19.1.2.8 Object.getOwnPropertySymbols ( O ) +JSTaggedValue BuiltinsObject::GetOwnPropertySymbols(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, GetOwnPropertySymbols); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + JSHandle obj = GetCallArg(argv, 0); + KeyType type = KeyType::SYMBOL_TYPE; + + // 1.Return GetOwnPropertyKeys(O, Symbol). + return GetOwnPropertyKeys(argv->GetThread(), obj, type); +} + +// 19.1.2.9 Object.getPrototypeOf ( O ) +JSTaggedValue BuiltinsObject::GetPrototypeOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, GetPrototypeOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1.Let obj be ToObject(O). + JSHandle func = GetCallArg(argv, 0); + + JSHandle obj = JSTaggedValue::ToObject(thread, func); + + // 2.ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3.Return obj.[[GetPrototypeOf]](). + return obj->GetPrototype(thread); +} + +// 19.1.2.10 Object.is ( value1, value2 ) +JSTaggedValue BuiltinsObject::Is(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Is); + + // 1.Return SameValue(value1, value2). + bool result = JSTaggedValue::SameValue(GetCallArg(argv, 0), GetCallArg(argv, 1)); + return GetTaggedBoolean(result); +} + +// 19.1.2.11 Object.isExtensible ( O ) +JSTaggedValue BuiltinsObject::IsExtensible(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + // 1.If Type(O) is not Object, return false. + JSTaggedValue obj = GetCallArg(argv, 0).GetTaggedValue(); + if (!obj.IsObject()) { + return GetTaggedBoolean(false); + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 2.Return IsExtensible(O). + return GetTaggedBoolean(obj.IsExtensible(thread)); +} + +// 19.1.2.12 Object.isFrozen ( O ) +JSTaggedValue BuiltinsObject::IsFrozen(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // 1.If Type(O) is not Object, return true. + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsECMAObject()) { + return GetTaggedBoolean(true); + } + + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 2.Return TestIntegrityLevel(O, "frozen"). + bool status = JSObject::TestIntegrityLevel(thread, JSHandle(obj), IntegrityLevel::FROZEN); + return GetTaggedBoolean(status); +} + +// 19.1.2.13 Object.isSealed ( O ) +JSTaggedValue BuiltinsObject::IsSealed(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + + // 1.If Type(O) is not Object, return true. + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsECMAObject()) { + return GetTaggedBoolean(true); + } + + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 2.Return TestIntegrityLevel(O, "sealed"). + bool status = JSObject::TestIntegrityLevel(thread, JSHandle(obj), IntegrityLevel::SEALED); + return GetTaggedBoolean(status); +} + +// 19.1.2.14 Object.keys(O) +JSTaggedValue BuiltinsObject::Keys(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Keys); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let obj be ToObject(O). + JSHandle msg = GetCallArg(argv, 0); + + JSHandle obj = JSTaggedValue::ToObject(thread, msg); + + // 2. ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let nameList be EnumerableOwnNames(obj). + JSHandle nameList = JSObject::EnumerableOwnNames(thread, obj); + + // 4. ReturnIfAbrupt(nameList). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Return CreateArrayFromList(nameList). + JSHandle result = JSArray::CreateArrayFromList(thread, nameList); + return result.GetTaggedValue(); +} + +// 19.1.2.15 Object.preventExtensions(O) +JSTaggedValue BuiltinsObject::PreventExtensions(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, PreventExtensions); + // 1. If Type(O) is not Object, return O. + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsECMAObject()) { + return obj.GetTaggedValue(); + } + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + // 2. Let status be O.[[PreventExtensions]](). + bool status = JSTaggedValue::PreventExtensions(argv->GetThread(), obj); + + // 3. ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + + // 4. If status is false, throw a TypeError exception. + if (!status) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "PreventExtensions: preventExtensions failed", + JSTaggedValue::Exception()); + } + + // 5. Return O. + return obj.GetTaggedValue(); +} +// 19.1.2.16 Object.prototype + +// 19.1.2.17 Object.seal(O) +JSTaggedValue BuiltinsObject::Seal(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Seal); + + // 1. If Type(O) is not Object, return O. + JSHandle msg = GetCallArg(argv, 0); + if (!msg->IsECMAObject()) { + return msg.GetTaggedValue(); + } + + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 2. Let status be SetIntegrityLevel(O, "sealed"). + JSHandle object = JSTaggedValue::ToObject(thread, msg); + bool status = JSObject::SetIntegrityLevel(thread, object, IntegrityLevel::SEALED); + + // 3. ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. If status is false, throw a TypeError exception. + if (!status) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "Seal: seal failed", JSTaggedValue::Exception()); + } + + // 5. Return O. + return object.GetTaggedValue(); +} + +// 19.1.2.18 Object.setPrototypeOf(O, proto) +JSTaggedValue BuiltinsObject::SetPrototypeOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, SetPrototypeOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be RequireObjectCoercible(O). + JSHandle object = JSTaggedValue::RequireObjectCoercible(thread, GetCallArg(argv, 0)); + + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If Type(proto) is neither Object nor Null, throw a TypeError exception. + JSHandle proto = GetCallArg(argv, 1); + if (!proto->IsNull() && !proto->IsECMAObject()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "SetPrototypeOf: proto is neither Object nor Null", + JSTaggedValue::Exception()); + } + + // 4. If Type(O) is not Object, return O. + if (!object->IsECMAObject()) { + return object.GetTaggedValue(); + } + + // 5. Let status be O.[[SetPrototypeOf]](proto). + bool status = JSTaggedValue::SetPrototype(thread, object, proto); + + // 6. ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. If status is false, throw a TypeError exception. + if (!status) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "SetPrototypeOf: prototype set failed", JSTaggedValue::Exception()); + } + + // 8. Return O. + return object.GetTaggedValue(); +} + +// 19.1.3.1 Object.prototype.constructor + +// 19.1.3.2 Object.prototype.hasOwnProperty(V) +JSTaggedValue BuiltinsObject::HasOwnProperty(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, HasOwnProperty); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let P be ToPropertyKey(V). + JSHandle prop = GetCallArg(argv, 0); + JSHandle property = JSTaggedValue::ToPropertyKey(thread, prop); + + // 2. ReturnIfAbrupt(P). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let O be ToObject(this value). + JSHandle object = JSTaggedValue::ToObject(thread, GetThis(argv)); + + // 4. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Return HasOwnProperty(O, P). + bool res = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(object), property); + return GetTaggedBoolean(res); +} + +// 19.1.3.3 Object.prototype.isPrototypeOf(V) +JSTaggedValue BuiltinsObject::IsPrototypeOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, IsPrototypeOf); + JSThread *thread = argv->GetThread(); + // 1. If Type(V) is not Object, return false. + JSHandle msg = GetCallArg(argv, 0); + if (!msg->IsECMAObject()) { + return GetTaggedBoolean(false); + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 2. Let O be ToObject(this value). + JSHandle object = JSTaggedValue::ToObject(thread, GetThis(argv)); + // 3. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Repeat + // a. Let V be V.[[GetPrototypeOf]](). + // b. If V is null, return false + // c. If SameValue(O, V) is true, return true. + JSTaggedValue msgValue = msg.GetTaggedValue(); + while (!msgValue.IsNull()) { + if (JSTaggedValue::SameValue(object.GetTaggedValue(), msgValue)) { + return GetTaggedBoolean(true); + } + msgValue = JSObject::Cast(msgValue)->GetPrototype(thread); + } + return GetTaggedBoolean(false); +} + +// 19.1.3.4 Object.prototype.propertyIsEnumerable(V) +JSTaggedValue BuiltinsObject::PropertyIsEnumerable(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // 1. Let P be ToPropertyKey(V). + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSHandle property = JSTaggedValue::ToPropertyKey(thread, msg); + + // 2. ReturnIfAbrupt(P). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let O be ToObject(this value). + JSHandle object = JSTaggedValue::ToObject(thread, GetThis(argv)); + // 4. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let desc be O.[[GetOwnProperty]](P). + PropertyDescriptor desc(thread); + JSTaggedValue::GetOwnProperty(thread, JSHandle::Cast(object), property, desc); + + // 6. ReturnIfAbrupt(desc). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. If desc is undefined, return false. + if (desc.IsEmpty()) { + return GetTaggedBoolean(false); + } + + // 8. Return the value of desc.[[Enumerable]]. + return GetTaggedBoolean(desc.IsEnumerable()); +} + +// 19.1.3.5 Object.prototype.toLocaleString([reserved1[, reserved2]]) +JSTaggedValue BuiltinsObject::ToLocaleString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ToLocaleString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle object = GetThis(argv); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + + // 2. Return Invoke(O, "toString"). + JSHandle calleeKey = thread->GlobalConstants()->GetHandledToStringString(); + return JSFunction::Invoke(thread, object, calleeKey, BuiltinsBase::GetArgsArray(argv)); +} + +JSTaggedValue BuiltinsObject::GetBuiltinTag(JSThread *thread, const JSHandle &object) +{ + BUILTINS_API_TRACE(thread, Object, GetBuiltinTag); + // 4. Let isArray be IsArray(O). + bool isArray = object->IsJSArray(); + // 5. ReturnIfAbrupt(isArray). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle builtinTag = thread->GetEcmaVM()->GetFactory()->NewFromString("Object"); + // 6. If isArray is true, let builtinTag be "Array". + if (isArray) { + builtinTag = thread->GetEcmaVM()->GetFactory()->NewFromString("Array"); + } else if (object->IsJSPrimitiveRef()) { + // 7. Else, if O is an exotic String object, let builtinTag be "String". + JSPrimitiveRef *primitiveRef = JSPrimitiveRef::Cast(*object); + if (primitiveRef->IsString()) { + builtinTag = thread->GetEcmaVM()->GetFactory()->NewFromString("String"); + } else if (primitiveRef->IsBoolean()) { + // 11. Else, if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean". + builtinTag = thread->GetEcmaVM()->GetFactory()->NewFromString("Boolean"); + } else if (primitiveRef->IsNumber()) { + // 12. Else, if O has a [[NumberData]] internal slot, let builtinTag be "Number". + builtinTag = thread->GetEcmaVM()->GetFactory()->NewFromString("Number"); + } + } else if (object->IsArguments()) { + builtinTag = thread->GetEcmaVM()->GetFactory()->NewFromString("Arguments"); + } else if (object->IsCallable()) { + builtinTag = thread->GetEcmaVM()->GetFactory()->NewFromString("Function"); + } else if (object->IsJSError()) { + builtinTag = thread->GetEcmaVM()->GetFactory()->NewFromString("Error"); + } else if (object->IsDate()) { + builtinTag = thread->GetEcmaVM()->GetFactory()->NewFromString("Date"); + } else if (object->IsJSRegExp()) { + builtinTag = thread->GetEcmaVM()->GetFactory()->NewFromString("RegExp"); + } + // 15. Else, let builtinTag be "Object". + return builtinTag.GetTaggedValue(); +} + +// 19.1.3.6 Object.prototype.toString() +JSTaggedValue BuiltinsObject::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If the this value is undefined, return "[object Undefined]". + + JSHandle msg = GetThis(argv); + if (msg->IsUndefined()) { + return GetTaggedString(thread, "[object Undefined]"); + } + // 2. If the this value is null, return "[object Null]". + if (msg->IsNull()) { + return GetTaggedString(thread, "[object Null]"); + } + + // 3. Let O be ToObject(this value). + JSHandle object = JSTaggedValue::ToObject(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle builtinTag(thread, GetBuiltinTag(thread, object)); + + // 16. Let tag be Get (O, @@toStringTag). + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + auto factory = ecmaVm->GetFactory(); + + JSHandle tag = JSTaggedValue::GetProperty(thread, msg, env->GetToStringTagSymbol()).GetValue(); + + // 17. ReturnIfAbrupt(tag). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 18. If Type(tag) is not String, let tag be builtinTag. + if (!tag->IsString()) { + tag = builtinTag; + } + + // 19. Return the String that is the result of concatenating "[object ", tag, and "]". + JSHandle leftString(factory->NewFromString("[object ")); + JSHandle rightString(factory->NewFromString("]")); + + JSHandle newLeftStringHandle = + factory->ConcatFromString(leftString, JSTaggedValue::ToString(thread, tag)); + auto result = factory->ConcatFromString(newLeftStringHandle, rightString); + return result.GetTaggedValue(); +} + +// 19.1.3.7 Object.prototype.valueOf() +JSTaggedValue BuiltinsObject::ValueOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ValueOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Return ToObject(this value). + JSHandle object = JSTaggedValue::ToObject(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return object.GetTaggedValue(); +} +// B.2.2.1 Object.prototype.__proto__ +JSTaggedValue BuiltinsObject::ProtoGetter(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ProtoGetter); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1.Let obj be ToObject(this value). + JSHandle obj = JSTaggedValue::ToObject(thread, GetThis(argv)); + + // 2.ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3.Return obj.[[GetPrototypeOf]](). + return obj->GetPrototype(thread); +} +JSTaggedValue BuiltinsObject::ProtoSetter(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ProtoSetter); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be RequireObjectCoercible(this value). + JSHandle obj = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)); + + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If Type(proto) is neither Object nor Null, return undefined.. + JSHandle proto = GetCallArg(argv, 0); + if (!proto->IsNull() && !proto->IsECMAObject()) { + return JSTaggedValue::Undefined(); + } + + // 4. If Type(O) is not Object, return undefined. + if (!obj->IsECMAObject()) { + return JSTaggedValue::Undefined(); + } + + // 5. Let status be O.[[SetPrototypeOf]](proto). + bool status = JSTaggedValue::SetPrototype(thread, obj, proto); + + // 6. ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. If status is false, throw a TypeError exception. + if (!status) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "ProtoSetter: proto set failed", JSTaggedValue::Exception()); + } + + // 8. Return O. + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsObject::CreateRealm(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle realm = factory->NewJSRealm(); + return realm.GetTaggedValue(); +} + +JSTaggedValue BuiltinsObject::Entries(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let obj be ? ToObject(O). + JSHandle obj = GetCallArg(argv, 0); + JSHandle object = JSTaggedValue::ToObject(thread, obj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key+value). + JSHandle nameList = JSObject::EnumerableOwnPropertyNames(thread, object, PropertyKind::KEY_VALUE); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return CreateArrayFromList(nameList). + return JSArray::CreateArrayFromList(thread, nameList).GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_object.h b/ecmascript/builtins/builtins_object.h new file mode 100644 index 0000000000000000000000000000000000000000..6fa45842a1c6e264da8a223f5e15f10913e934ec --- /dev/null +++ b/ecmascript/builtins/builtins_object.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_OBJECT_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_OBJECT_H + +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" + +namespace panda::ecmascript::builtins { +enum class KeyType : uint8_t { + STRING_TYPE = 0, + SYMBOL_TYPE, +}; + +class BuiltinsObject : public base::BuiltinsBase { +public: + // 19.1.1.1Object ( [ value ] ) + static JSTaggedValue ObjectConstructor(EcmaRuntimeCallInfo *argv); + + // 19.1.2.1Object.assign ( target, ...sources ) + static JSTaggedValue Assign(EcmaRuntimeCallInfo *argv); + // 19.1.2.2Object.create ( O [ , Properties ] ) + static JSTaggedValue Create(EcmaRuntimeCallInfo *argv); + // 19.1.2.3Object.defineProperties ( O, Properties ) + static JSTaggedValue DefineProperties(EcmaRuntimeCallInfo *argv); + // 19.1.2.4Object.defineProperty ( O, P, Attributes ) + static JSTaggedValue DefineProperty(EcmaRuntimeCallInfo *argv); + // 19.1.2.5Object.freeze ( O ) + static JSTaggedValue Freeze(EcmaRuntimeCallInfo *argv); + // 19.1.2.6Object.getOwnPropertyDescriptor ( O, P ) + static JSTaggedValue GetOwnPropertyDesciptor(EcmaRuntimeCallInfo *argv); + // 19.1.2.7Object.getOwnPropertyNames ( O ) + static JSTaggedValue GetOwnPropertyNames(EcmaRuntimeCallInfo *argv); + // 19.1.2.8Object.getOwnPropertySymbols ( O ) + static JSTaggedValue GetOwnPropertySymbols(EcmaRuntimeCallInfo *argv); + // 19.1.2.9Object.getPrototypeOf ( O ) + static JSTaggedValue GetPrototypeOf(EcmaRuntimeCallInfo *argv); + // 19.1.2.10Object.is ( value1, value2 ) + static JSTaggedValue Is(EcmaRuntimeCallInfo *argv); + // 19.1.2.11Object.isExtensible ( O ) + static JSTaggedValue IsExtensible(EcmaRuntimeCallInfo *argv); + // 19.1.2.12Object.isFrozen ( O ) + static JSTaggedValue IsFrozen(EcmaRuntimeCallInfo *argv); + // 19.1.2.13Object.isSealed ( O ) + static JSTaggedValue IsSealed(EcmaRuntimeCallInfo *argv); + // 19.1.2.14 Object.keys(O) + static JSTaggedValue Keys(EcmaRuntimeCallInfo *argv); + // 19.1.2.15 Object.preventExtensions(O) + static JSTaggedValue PreventExtensions(EcmaRuntimeCallInfo *argv); + // 19.1.2.17 Object.seal(O) + static JSTaggedValue Seal(EcmaRuntimeCallInfo *argv); + // 19.1.2.18 Object.setPrototypeOf(O, proto) + static JSTaggedValue SetPrototypeOf(EcmaRuntimeCallInfo *argv); + + // 19.1.3.2 Object.prototype.hasOwnProperty(V) + static JSTaggedValue HasOwnProperty(EcmaRuntimeCallInfo *argv); + // 19.1.3.3 Object.prototype.isPrototypeOf(V) + static JSTaggedValue IsPrototypeOf(EcmaRuntimeCallInfo *argv); + // 19.1.3.4 Object.prototype.propertyIsEnumerable(V) + static JSTaggedValue PropertyIsEnumerable(EcmaRuntimeCallInfo *argv); + // 19.1.3.5 Object.prototype.toLocaleString([reserved1[, reserved2]]) + static JSTaggedValue ToLocaleString(EcmaRuntimeCallInfo *argv); + // 19.1.3.6 Object.prototype.toString() + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 19.1.3.7 Object.prototype.valueOf() + static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue CreateRealm(EcmaRuntimeCallInfo *argv); + // 20.1.2.5 Object.entries ( O ) + static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); + + // B.2.2.1 Object.prototype.__proto__ + static JSTaggedValue ProtoGetter(EcmaRuntimeCallInfo *argv); + static JSTaggedValue ProtoSetter(EcmaRuntimeCallInfo *argv); + +private: + static JSTaggedValue ObjectDefineProperties(JSThread *thread, const JSHandle &obj, + const JSHandle &prop); + static JSTaggedValue GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj, const KeyType &type); + static JSTaggedValue GetBuiltinTag(JSThread *thread, const JSHandle &object); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_H diff --git a/ecmascript/builtins/builtins_promise.cpp b/ecmascript/builtins/builtins_promise.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ae2f4dba6c9549a7cb26baff008eb0cd1a95a6f8 --- /dev/null +++ b/ecmascript/builtins/builtins_promise.cpp @@ -0,0 +1,632 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_promise.h" +#include "ecmascript/builtins/builtins_promise_handler.h" +#include "ecmascript/builtins/builtins_promise_job.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/jobs/micro_job_queue.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/assert_scope-inl.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript::builtins { +using BuiltinsPromiseJob = builtins::BuiltinsPromiseJob; +// 25.4.3.1 Promise ( executor ) +JSTaggedValue BuiltinsPromise::PromiseConstructor([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 1. If NewTarget is undefined, throw a TypeError exception. + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "PromiseConstructor: NewTarget is undefined", JSTaggedValue::Exception()); + } + // 2. If IsCallable(executor) is false, throw a TypeError exception. + JSHandle executor = BuiltinsBase::GetCallArg(argv, 0); + if (!executor->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "PromiseConstructor: executor is not callable", JSTaggedValue::Exception()); + } + + // 3. Let promise be OrdinaryCreateFromConstructor(NewTarget, "%PromisePrototype%", + // «[[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]]» ). + // 4. ReturnIfAbrupt(promise). + JSHandle constructor = GetConstructor(argv); + JSHandle instancePromise = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Set promise's [[PromiseState]] internal slot to "pending". + // 6. Set promise's [[PromiseFulfillReactions]] internal slot to a new empty List. + // 7. Set promise's [[PromiseRejectReactions]] internal slot to a new empty List. + // 8. Let resolvingFunctions be CreateResolvingFunctions(promise). + JSHandle resolvingFunction = JSPromise::CreateResolvingFunctions(thread, instancePromise); + // 9. Let completion be Call(executor, undefined, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[reject]]) + JSHandle arguments = factory->NewTaggedArray(2); // 2: 2 means two args stored in array + auto result = resolvingFunction->GetResolveFunction(); + arguments->Set(thread, 0, result); + result = resolvingFunction->GetRejectFunction(); + arguments->Set(thread, 1, result); + + JSHandle thisValue = globalConst->GetHandledUndefined(); + JSTaggedValue taggedValue = JSFunction::Call(thread, executor, thisValue, arguments); + JSHandle completionValue(thread, taggedValue); + + // 10. If completion is an abrupt completion, then + // a. Let status be Call(resolvingFunctions.[[Reject]], undefined, «completion.[[value]]»). + // b. ReturnIfAbrupt(status). + if (thread->HasPendingException()) { + completionValue = JSPromise::IfThrowGetThrowValue(thread); + thread->ClearException(); + array_size_t length = 1; + JSHandle arrayCompletion = factory->NewTaggedArray(length); + arrayCompletion->Set(thread, 0, completionValue); + JSHandle reject(thread, resolvingFunction->GetRejectFunction()); + JSFunction::Call(thread, reject, thisValue, arrayCompletion); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + // 11. Return promise. + return instancePromise.GetTaggedValue(); +} + +// 25.4.4.1 Promise.all ( iterable ) +JSTaggedValue BuiltinsPromise::All(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, All); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. Let C be the this value. + JSHandle ctor = GetThis(argv); + // 2. If Type(C) is not Object, throw a TypeError exception. + if (!ctor->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Promise ALL: this value is not object", JSTaggedValue::Exception()); + } + // 3. Let S be Get(C, @@species). + // 4. ReturnIfAbrupt(S). + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle sctor = JSObject::GetProperty(thread, ctor, speciesSymbol).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, sctor.GetTaggedValue()); + + // 5. If S is neither undefined nor null, let C be S. + if (!sctor->IsUndefined() && !sctor->IsNull()) { + ctor = sctor; + } + // 6. Let promiseCapability be NewPromiseCapability(C). + JSHandle capa = JSPromise::NewPromiseCapability(thread, ctor); + // 7. ReturnIfAbrupt(promiseCapability). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, capa.GetTaggedValue()); + // 8. Let iterator be GetIterator(iterable). + JSHandle itor = JSIterator::GetIterator(thread, GetCallArg(argv, 0)); + // 9. IfAbruptRejectPromise(iterator, promiseCapability). + if (thread->HasPendingException()) { + itor = JSPromise::IfThrowGetThrowValue(thread); + } + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, itor, capa); + + // 10. Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}. + JSHandle done(thread, JSTaggedValue::False()); + JSHandle itRecord = factory->NewPromiseIteratorRecord(itor, done); + // 11. Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability). + JSHandle result = PerformPromiseAll(thread, itRecord, ctor, capa); + // 12. If result is an abrupt completion, + if (result->IsThrow()) { + thread->ClearException(); + // a. If iteratorRecord.[[done]] is false, let result be IteratorClose(iterator, result). + // b. IfAbruptRejectPromise(result, promiseCapability). + if (itRecord->GetDone().IsFalse()) { + JSHandle closeVal = + JSIterator::IteratorClose(thread, itor, JSHandle::Cast(result)); + if (closeVal.GetTaggedValue().IsRecord()) { + result = JSHandle::Cast(closeVal); + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa); + return result->GetValue(); + } + } + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa); + return result->GetValue(); + } + // 13. Return Completion(result). + return result->GetValue(); +} + +// 25.4.4.3 Promise.race ( iterable ) +JSTaggedValue BuiltinsPromise::Race(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, Race); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle env = ecmaVm->GetGlobalEnv(); + // 1. Let C be the this value. + // 2. If Type(C) is not Object, throw a TypeError exception. + JSHandle thisValue = GetThis(argv); + if (!thisValue->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Race: this value is not object", JSTaggedValue::Exception()); + } + // 3. Let S be Get(C, @@species). + // 4. ReturnIfAbrupt(S). + // 5. If S is neither undefined nor null, let C be S. + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesConstructor = JSObject::GetProperty(thread, thisValue, speciesSymbol).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!(speciesConstructor->IsUndefined() || speciesConstructor->IsNull())) { + thisValue = speciesConstructor; + } + + // 6. Let promiseCapability be NewPromiseCapability(C). + // 7. ReturnIfAbrupt(promiseCapability). + JSHandle promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 8. Let iterator be GetIterator(iterable). + // 9. IfAbruptRejectPromise(iterator, promiseCapability). + JSHandle iterable = GetCallArg(argv, 0); + JSHandle iterator = JSIterator::GetIterator(thread, iterable); + if (thread->HasPendingException()) { + iterator = JSPromise::IfThrowGetThrowValue(thread); + } + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, iterator, promiseCapability); + + // 10. Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}. + JSHandle done(thread, JSTaggedValue::False()); + JSHandle iteratorRecord = factory->NewPromiseIteratorRecord(iterator, done); + + // 11. Let result be PerformPromiseRace(iteratorRecord, promiseCapability, C). + // 12. If result is an abrupt completion, then + // a. If iteratorRecord.[[done]] is false, let result be IteratorClose(iterator,result). + // b. IfAbruptRejectPromise(result, promiseCapability). + // 13. Return Completion(result). + JSHandle result = PerformPromiseRace(thread, iteratorRecord, promiseCapability, thisValue); + if (result->IsThrow()) { + thread->ClearException(); + if (iteratorRecord->GetDone().IsFalse()) { + JSHandle value = + JSIterator::IteratorClose(thread, iterator, JSHandle::Cast(result)); + if (value.GetTaggedValue().IsCompletionRecord()) { + result = JSHandle(value); + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); + return result->GetValue(); + } + } + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); + return result->GetValue(); + } + return result->GetValue(); +} + +// 25.4.4.5 Promise.resolve ( x ) +JSTaggedValue BuiltinsPromise::Resolve(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, Resolve); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + auto ecmaVm = thread->GetEcmaVM(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = ecmaVm->GetFactory(); + // 1. Let C be the this value. + JSHandle thisValue = GetThis(argv); + // 2. If Type(C) is not Object, throw a TypeError exception. + if (!thisValue->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Resolve: this value is not object", JSTaggedValue::Exception()); + } + // 3. If IsPromise(x) is true, + // a. Let xConstructor be Get(x, "constructor"). + // b. ReturnIfAbrupt(xConstructor). + // c. If SameValue(xConstructor, C) is true, return x. + JSHandle xValue = BuiltinsBase::GetCallArg(argv, 0); + if (xValue->IsJSPromise()) { + JSHandle ctorKey(globalConst->GetHandledConstructorString()); + JSHandle ctorValue = JSObject::GetProperty(thread, xValue, ctorKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (JSTaggedValue::SameValue(ctorValue.GetTaggedValue(), thisValue.GetTaggedValue())) { + JSHandle value = JSHandle::Cast(xValue); + return value.GetTaggedValue(); + } + } + // 4. Let promiseCapability be NewPromiseCapability(C). + // 5. ReturnIfAbrupt(promiseCapability). + JSHandle promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. Let resolveResult be Call(promiseCapability.[[Resolve]], undefined, «x»). + // 7. ReturnIfAbrupt(resolveResult). + array_size_t length = 1; + JSHandle arrayCompletion = factory->NewTaggedArray(length); + arrayCompletion->Set(thread, 0, xValue); + JSHandle resolve(thread, promiseCapability->GetResolve()); + JSHandle undefined = globalConst->GetHandledUndefined(); + JSFunction::Call(thread, resolve, undefined, arrayCompletion); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 8. Return promiseCapability.[[Promise]]. + JSHandle promise(thread, promiseCapability->GetPromise()); + return promise.GetTaggedValue(); +} + +// 25.4.4.4 Promise.reject ( r ) +JSTaggedValue BuiltinsPromise::Reject(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, Reject); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. Let C be the this value. + // 2. If Type(C) is not Object, throw a TypeError exception. + JSHandle thisValue = GetThis(argv); + if (!thisValue->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reject: this value is not object", JSTaggedValue::Exception()); + } + + // 3. Let promiseCapability be NewPromiseCapability(C). + // 4. ReturnIfAbrupt(promiseCapability). + JSHandle promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let rejectResult be Call(promiseCapability.[[Reject]], undefined, «r»). + // 6. ReturnIfAbrupt(rejectResult). + JSHandle reason = GetCallArg(argv, 0); + array_size_t length = 1; + JSHandle arrayCompletion = factory->NewTaggedArray(length); + arrayCompletion->Set(thread, 0, reason); + JSHandle reject(thread, promiseCapability->GetReject()); + JSHandle undefined = globalConst->GetHandledUndefined(); + JSFunction::Call(thread, reject, undefined, arrayCompletion); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. Return promiseCapability.[[Promise]]. + JSHandle promise(thread, promiseCapability->GetPromise()); + return promise.GetTaggedValue(); +} + +// 25.4.4.6 get Promise [ @@species ] +JSTaggedValue BuiltinsPromise::GetSpecies([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return JSTaggedValue(GetThis(argv).GetTaggedValue()); +} + +// 25.4.5.1 Promise.prototype.catch ( onRejected ) +JSTaggedValue BuiltinsPromise::Catch(EcmaRuntimeCallInfo *argv) +{ + // 1. Let promise be the this value. + // 2. Return Invoke(promise, "then", «undefined, onRejected»). + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, Catch); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle promise = GetThis(argv); + JSHandle thenKey = globalConst->GetHandledPromiseThenString(); + JSHandle reject = GetCallArg(argv, 0); + array_size_t length = 2; + JSHandle arrayList = factory->NewTaggedArray(length); + arrayList->Set(thread, 0, globalConst->GetHandledUndefined()); + arrayList->Set(thread, 1, reject); + return JSFunction::Invoke(thread, promise, thenKey, arrayList); +} + +// 25.4.5.3 Promise.prototype.then ( onFulfilled , onRejected ) +JSTaggedValue BuiltinsPromise::Then(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, Then); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + + // 1. Let promise be the this value. + JSHandle thisValue = GetThis(argv); + // 2. If IsPromise(promise) is false, throw a TypeError exception. + if (!thisValue->IsJSPromise()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Then: thisValue is not promise!", JSTaggedValue::Exception()); + } + // 3. Let C be SpeciesConstructor(promise, %Promise%). + // 4. ReturnIfAbrupt(C). + JSHandle promise = JSHandle::Cast(thisValue); + JSHandle defaultFunc = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle constructor = JSObject::SpeciesConstructor(thread, promise, defaultFunc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let resultCapability be NewPromiseCapability(C). + // 6. ReturnIfAbrupt(resultCapability). + JSHandle resultCapability = JSPromise::NewPromiseCapability(thread, constructor); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle onFulfilled = BuiltinsBase::GetCallArg(argv, 0); + JSHandle onRejected = BuiltinsBase::GetCallArg(argv, 1); + + // 7. Return PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability). + return PerformPromiseThen(thread, JSHandle::Cast(promise), onFulfilled, onRejected, resultCapability); +} + +JSTaggedValue BuiltinsPromise::PerformPromiseThen(JSThread *thread, const JSHandle &promise, + const JSHandle &onFulfilled, + const JSHandle &onRejected, + const JSHandle &capability) +{ + auto ecmaVm = thread->GetEcmaVM(); + JSHandle job = ecmaVm->GetMicroJobQueue(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSMutableHandle fulfilled(thread, onFulfilled.GetTaggedValue()); + auto globalConst = thread->GlobalConstants(); + if (!fulfilled->IsCallable()) { + fulfilled.Update(globalConst->GetIdentityString()); + } + JSMutableHandle rejected(thread, onRejected.GetTaggedValue()); + if (!rejected->IsCallable()) { + rejected.Update(globalConst->GetThrowerString()); + } + JSHandle fulfillReaction = factory->NewPromiseReaction(); + fulfillReaction->SetPromiseCapability(thread, capability.GetTaggedValue()); + fulfillReaction->SetHandler(thread, fulfilled.GetTaggedValue()); + + JSHandle rejectReaction = factory->NewPromiseReaction(); + rejectReaction->SetPromiseCapability(thread, capability.GetTaggedValue()); + rejectReaction->SetHandler(thread, rejected.GetTaggedValue()); + + if (JSTaggedValue::SameValue(promise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING)))) { + JSHandle fulfillReactions(thread, promise->GetPromiseFulfillReactions()); + auto result = + JSTaggedValue(TaggedQueue::Push(thread, fulfillReactions, JSHandle::Cast(fulfillReaction))); + promise->SetPromiseFulfillReactions(thread, result); + + JSHandle rejectReactions(thread, promise->GetPromiseRejectReactions()); + result = + JSTaggedValue(TaggedQueue::Push(thread, rejectReactions, JSHandle::Cast(rejectReaction))); + promise->SetPromiseRejectReactions(thread, result); + } else if (JSTaggedValue::SameValue(promise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED)))) { + JSHandle argv = factory->NewTaggedArray(2); // 2: 2 means two args stored in array + argv->Set(thread, 0, fulfillReaction.GetTaggedValue()); + argv->Set(thread, 1, promise->GetPromiseResult()); + + JSHandle promiseReactionsJob(env->GetPromiseReactionJob()); + job->EnqueueJob(thread, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv); + } else if (JSTaggedValue::SameValue(promise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED)))) { + JSHandle argv = factory->NewTaggedArray(2); // 2: 2 means two args stored in array + argv->Set(thread, 0, rejectReaction.GetTaggedValue()); + argv->Set(thread, 1, promise->GetPromiseResult()); + + JSHandle promiseReactionsJob(env->GetPromiseReactionJob()); + job->EnqueueJob(thread, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv); + } + return capability->GetPromise(); +} + +JSHandle BuiltinsPromise::PerformPromiseAll(JSThread *thread, + const JSHandle &itRecord, + const JSHandle &ctor, + const JSHandle &capa) +{ + auto ecmaVm = thread->GetEcmaVM(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = ecmaVm->GetFactory(); + // 1. Assert: constructor is a constructor function. + ASSERT_PRINT(ctor->IsConstructor(), "PerformPromiseAll is not constructor"); + // 2. Assert: resultCapability is a PromiseCapability record. (not need) + // 3. Let values be a new empty List. + JSHandle values = factory->NewPromiseRecord(); + JSHandle emptyArray = factory->EmptyArray(); + values->SetValue(thread, emptyArray); + // 4. Let remainingElementsCount be a new Record { [[value]]: 1 }. + JSHandle remainCnt = factory->NewPromiseRecord(); + remainCnt->SetValue(thread, JSTaggedNumber(1)); + // 5. Let index be 0. + array_size_t index = 0; + // 6. Repeat + JSHandle itor(thread, itRecord->GetIterator()); + JSMutableHandle next(thread, globalConst->GetUndefined()); + while (true) { + // a. Let next be IteratorStep(iteratorRecord.[[iterator]]). + next.Update(JSIterator::IteratorStep(thread, itor).GetTaggedValue()); + // b. If next is an abrupt completion, set iteratorRecord.[[done]] to true. + if (thread->HasPendingException()) { + itRecord->SetDone(thread, JSTaggedValue::True()); + next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue()); + } + // c. ReturnIfAbrupt(next). + RETURN_COMPLETION_IF_ABRUPT(thread, next); + // d. If next is false, + if (next->IsFalse()) { + // i. Set iteratorRecord.[[done]] to true. + itRecord->SetDone(thread, JSTaggedValue::True()); + // ii. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] − 1. + remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue())); + // iii. If remainingElementsCount.[[value]] is 0, + if (remainCnt->GetValue().IsZero()) { + // 1. Let valuesArray be CreateArrayFromList(values). + JSHandle jsArrayValues = + JSArray::CreateArrayFromList(thread, JSHandle(thread, values->GetValue())); + // 2. Let resolveResult be Call(resultCapability.[[Resolve]], undefined, «valuesArray»). + JSHandle resCapaFunc(thread, capa->GetResolve()); + JSHandle argv = factory->NewTaggedArray(1); + argv->Set(thread, 0, jsArrayValues.GetTaggedValue()); + JSTaggedValue resolveRes = + JSFunction::Call(thread, resCapaFunc, globalConst->GetHandledUndefined(), argv); + // 3. ReturnIfAbrupt(resolveResult) + JSHandle resolveAbrupt(thread, resolveRes); + RETURN_COMPLETION_IF_ABRUPT(thread, resolveAbrupt); + } + // iv. Return resultCapability.[[Promise]]. + JSHandle resRecord = factory->NewCompletionRecord( + CompletionRecord::NORMAL, + JSHandle(thread, capa->GetPromise())); + return resRecord; + } + // e. Let nextValue be IteratorValue(next). + JSHandle nextVal = JSIterator::IteratorValue(thread, next); + // f. If nextValue is an abrupt completion, set iteratorRecord.[[done]] to true. + if (thread->HasPendingException()) { + itRecord->SetDone(thread, JSTaggedValue::True()); + if (thread->GetException().IsObjectWrapper()) { + JSHandle wrapperVal(thread, thread->GetException()); + JSHandle throwVal(thread, wrapperVal->GetValue()); + nextVal = throwVal; + } else { + nextVal = JSHandle(thread, thread->GetException()); + } + } + + // g. ReturnIfAbrupt(nextValue). + RETURN_COMPLETION_IF_ABRUPT(thread, nextVal); + // h. Append undefined to values. + JSHandle valuesArray = + JSHandle::Cast(JSHandle(thread, values->GetValue())); + valuesArray = TaggedArray::SetCapacity(thread, valuesArray, index + 1); + valuesArray->Set(thread, index, JSTaggedValue::Undefined()); + values->SetValue(thread, valuesArray); + // i. Let nextPromise be Invoke(constructor, "resolve", «‍nextValue»). + JSHandle resolveKey = globalConst->GetHandledPromiseResolveString(); + JSHandle nextValueArray = factory->NewTaggedArray(1); + nextValueArray->Set(thread, 0, nextVal); + JSTaggedValue taggedNextPromise = JSFunction::Invoke(thread, ctor, resolveKey, nextValueArray); + // j. ReturnIfAbrupt(nextPromise). + JSHandle nextPromise(thread, taggedNextPromise); + RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise); + // k. Let resolveElement be a new built-in function object as defined in Promise.all + // Resolve Element Functions. + JSHandle resoleveElement = factory->NewJSPromiseAllResolveElementFunction( + reinterpret_cast(BuiltinsPromiseHandler::ResolveElementFunction)); + // l. Set the [[AlreadyCalled]] internal slot of resolveElement to a new Record {[[value]]: false }. + JSHandle falseRecord = factory->NewPromiseRecord(); + falseRecord->SetValue(thread, JSTaggedValue::False()); + resoleveElement->SetAlreadyCalled(thread, falseRecord); + // m. Set the [[Index]] internal slot of resolveElement to index. + resoleveElement->SetIndex(thread, JSTaggedValue(index)); + // n. Set the [[Values]] internal slot of resolveElement to values. + resoleveElement->SetValues(thread, values); + // o. Set the [[Capabilities]] internal slot of resolveElement to resultCapability. + resoleveElement->SetCapabilities(thread, capa); + // p. Set the [[RemainingElements]] internal slot of resolveElement to remainingElementsCount. + resoleveElement->SetRemainingElements(thread, remainCnt); + // q. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] + 1. + remainCnt->SetValue(thread, ++JSTaggedNumber(remainCnt->GetValue())); + // r. Let result be Invoke(nextPromise, "then", «‍resolveElement, resultCapability.[[Reject]]»). + JSHandle thenKey = globalConst->GetHandledPromiseThenString(); + JSHandle arg = factory->NewTaggedArray(2); // 2: 2 means two args stored in array + arg->Set(thread, 0, resoleveElement); + arg->Set(thread, 1, capa->GetReject()); + JSTaggedValue taggedResult = JSFunction::Invoke(thread, nextPromise, thenKey, arg); + JSHandle result(thread, taggedResult); + // s. ReturnIfAbrupt(result). + RETURN_COMPLETION_IF_ABRUPT(thread, result); + // t. Set index to index + 1. + ++index; + } +} + +JSHandle BuiltinsPromise::PerformPromiseRace(JSThread *thread, + const JSHandle &iteratorRecord, + const JSHandle &capability, + const JSHandle &constructor) +{ + // 1. Repeat + // a. Let next be IteratorStep(iteratorRecord.[[iterator]]). + // b. If next is an abrupt completion, set iteratorRecord.[[done]] to true. + // c. ReturnIfAbrupt(next). + // d. If next is false, then + // i. Set iteratorRecord.[[done]] to true. + // ii. Return promiseCapability.[[Promise]]. + // e. Let nextValue be IteratorValue(next). + // f. If nextValue is an abrupt completion, set iteratorRecord.[[done]] to true. + // g. ReturnIfAbrupt(nextValue). + // h. Let nextPromise be Invoke(C, "resolve", «nextValue»). + // i. ReturnIfAbrupt(nextPromise). + // j. Let result be Invoke(nextPromise, "then", «promiseCapability.[[Resolve]], promiseCapability.[[Reject]]»). + // k. ReturnIfAbrupt(result). + auto ecmaVm = thread->GetEcmaVM(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle iterator(thread, iteratorRecord->GetIterator()); + JSMutableHandle next(thread, globalConst->GetUndefined()); + while (true) { + next.Update(JSIterator::IteratorStep(thread, iterator).GetTaggedValue()); + if (thread->HasPendingException()) { + iteratorRecord->SetDone(thread, JSTaggedValue::True()); + next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue()); + } + RETURN_COMPLETION_IF_ABRUPT(thread, next); + if (next->IsFalse()) { + iteratorRecord->SetDone(thread, JSTaggedValue::True()); + JSHandle promise(thread, capability->GetPromise()); + JSHandle completionRecord = + factory->NewCompletionRecord(CompletionRecord::NORMAL, promise); + return completionRecord; + } + JSHandle nextValue = JSIterator::IteratorValue(thread, next); + if (thread->HasPendingException()) { + iteratorRecord->SetDone(thread, JSTaggedValue::True()); + nextValue = JSPromise::IfThrowGetThrowValue(thread); + } + RETURN_COMPLETION_IF_ABRUPT(thread, nextValue); + JSHandle resolveStr = globalConst->GetHandledPromiseResolveString(); + array_size_t length = 1; + JSHandle array = factory->NewTaggedArray(length); + array->Set(thread, 0, nextValue); + JSTaggedValue result = JSFunction::Invoke(thread, constructor, resolveStr, array); + JSHandle nextPromise(thread, result); + if (thread->HasPendingException()) { + nextPromise = JSPromise::IfThrowGetThrowValue(thread); + } + RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise); + + JSHandle thenStr = globalConst->GetHandledPromiseThenString(); + length = 2; // 2: 2 means two args stored in array + array = factory->NewTaggedArray(length); + array->Set(thread, 0, capability->GetResolve()); + array->Set(thread, 1, capability->GetReject()); + result = JSFunction::Invoke(thread, nextPromise, thenStr, array); + JSHandle handleResult(thread, result); + if (thread->HasPendingException()) { + handleResult = JSPromise::IfThrowGetThrowValue(thread); + } + RETURN_COMPLETION_IF_ABRUPT(thread, handleResult); + } +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_promise.h b/ecmascript/builtins/builtins_promise.h new file mode 100644 index 0000000000000000000000000000000000000000..ba68cc8217e7c2d7d6fadc444353486efac3cc84 --- /dev/null +++ b/ecmascript/builtins/builtins_promise.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_PROMISE_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_PROMISE_H + +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript::builtins { +class BuiltinsPromise : public base::BuiltinsBase { +public: + // 25.4.3.1 Promise ( executor ) + static JSTaggedValue PromiseConstructor(EcmaRuntimeCallInfo *argv); + // 25.4.4.1 Promise.all ( iterable ) + static JSTaggedValue All(EcmaRuntimeCallInfo *argv); + // 25.4.4.3 Promise.race ( iterable ) + static JSTaggedValue Race(EcmaRuntimeCallInfo *argv); + // 25.4.4.4 Promise.reject ( r ) + static JSTaggedValue Reject(EcmaRuntimeCallInfo *argv); + // 25.4.4.5 Promise.resolve ( x ) + static JSTaggedValue Resolve(EcmaRuntimeCallInfo *argv); + // 25.4.4.6 get Promise [ @@species ] + static JSTaggedValue GetSpecies(EcmaRuntimeCallInfo *argv); + // 25.4.5.1 Promise.prototype.catch ( onRejected ) + static JSTaggedValue Catch(EcmaRuntimeCallInfo *argv); + // 25.4.5.3 Promise.prototype.then ( onFulfilled , onRejected ) + static JSTaggedValue Then(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue PerformPromiseThen(JSThread *thread, const JSHandle &promise, + const JSHandle &onFulfilled, + const JSHandle &onRejected, + const JSHandle &capability); + +private: + static JSHandle PerformPromiseAll(JSThread *thread, + const JSHandle &itRecord, + const JSHandle &ctor, + const JSHandle &capa); + + static JSHandle PerformPromiseRace(JSThread *thread, + const JSHandle &iteratorRecord, + const JSHandle &capability, + const JSHandle &constructor); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_PROMISE_H \ No newline at end of file diff --git a/ecmascript/builtins/builtins_promise_handler.cpp b/ecmascript/builtins/builtins_promise_handler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..83728d933ae4c24ea65d7de1f48a962e71c7f275 --- /dev/null +++ b/ecmascript/builtins/builtins_promise_handler.cpp @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_promise_handler.h" +#include "ecmascript/global_env.h" +#include "ecmascript/jobs/micro_job_queue.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_async_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/mem/assert_scope-inl.h" + +namespace panda::ecmascript::builtins { +// es6 25.4.1.3.2 Promise Resolve Functions +JSTaggedValue BuiltinsPromiseHandler::Resolve(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, Resolve); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + + // 1. Assert: F has a [[Promise]] internal slot whose value is an Object. + JSHandle resolve = JSHandle::Cast(GetConstructor(argv)); + ASSERT_PRINT(resolve->GetPromise().IsECMAObject(), "Resolve: promise must be js object"); + + // 2. Let promise be the value of F's [[Promise]] internal slot. + // 3. Let alreadyResolved be the value of F's [[AlreadyResolved]] internal slot. + // 4. If alreadyResolved.[[value]] is true, return undefined. + // 5. Set alreadyResolved.[[value]] to true. + JSHandle resolvePromise(thread, resolve->GetPromise()); + JSHandle alreadyResolved(thread, resolve->GetAlreadyResolved()); + if (alreadyResolved->GetValue().IsTrue()) { + return JSTaggedValue::Undefined(); + } + alreadyResolved->SetValue(thread, JSTaggedValue::True()); + + // 6. If SameValue(resolution, promise) is true, then + // a. Let selfResolutionError be a newly created TypeError object. + // b. Return RejectPromise(promise, selfResolutionError). + JSHandle resolution = BuiltinsBase::GetCallArg(argv, 0); + if (JSTaggedValue::SameValue(resolution.GetTaggedValue(), resolvePromise.GetTaggedValue())) { + JSHandle resolutionError = + factory->GetJSError(ErrorType::TYPE_ERROR, "Resolve: The promise and resolution cannot be the same."); + JSPromise::RejectPromise(thread, resolvePromise, JSHandle::Cast(resolutionError)); + return JSTaggedValue::Undefined(); + } + // 7. If Type(resolution) is not Object, then + // a. Return FulfillPromise(promise, resolution). + if (!resolution.GetTaggedValue().IsECMAObject()) { + JSPromise::FulfillPromise(thread, resolvePromise, resolution); + return JSTaggedValue::Undefined(); + } + // 8. Let then be Get(resolution, "then"). + // 9. If then is an abrupt completion, then + // a. Return RejectPromise(promise, then.[[value]]). + JSHandle thenKey(thread->GlobalConstants()->GetHandledPromiseThenString()); + JSHandle thenValue = JSObject::GetProperty(thread, resolution, thenKey).GetValue(); + if (thread->HasPendingException()) { + if (thread->GetException().IsObjectWrapper()) { + JSHandle wrapperValue(thread, thread->GetException()); + JSHandle throwValue(thread, wrapperValue->GetValue()); + thenValue = throwValue; + } + thread->ClearException(); + return JSPromise::RejectPromise(thread, resolvePromise, thenValue); + } + // 10. Let thenAction be then.[[value]]. + // 11. If IsCallable(thenAction) is false, then + // a. Return FulfillPromise(promise, resolution). + if (!thenValue->IsCallable()) { + JSPromise::FulfillPromise(thread, resolvePromise, resolution); + return JSTaggedValue::Undefined(); + } + // 12. Perform EnqueueJob ("PromiseJobs", PromiseResolveThenableJob, «promise, resolution, thenAction») + JSHandle arguments = factory->NewTaggedArray(3); // 3: 3 means three args stored in array + arguments->Set(thread, 0, resolvePromise); + arguments->Set(thread, 1, resolution); + arguments->Set(thread, 2, thenValue); // 2: 2 means index of array is 2 + + JSHandle promiseResolveThenableJob(env->GetPromiseResolveThenableJob()); + JSHandle job = thread->GetEcmaVM()->GetMicroJobQueue(); + job->EnqueueJob(thread, job::QueueType::QUEUE_PROMISE, promiseResolveThenableJob, arguments); + + // 13. Return undefined. + return JSTaggedValue::Undefined(); +} + +// es6 25.4.1.3.1 Promise Reject Functions +JSTaggedValue BuiltinsPromiseHandler::Reject(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, Reject); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Assert: F has a [[Promise]] internal slot whose value is an Object. + JSHandle reject = JSHandle::Cast(GetConstructor(argv)); + ASSERT_PRINT(reject->GetPromise().IsECMAObject(), "Reject: promise must be js object"); + + // 2. Let promise be the value of F's [[Promise]] internal slot. + // 3. Let alreadyResolved be the value of F's [[AlreadyResolved]] internal slot. + // 4. If alreadyResolved.[[value]] is true, return undefined. + // 5. Set alreadyResolved.[[value]] to true. + JSHandle rejectPromise(thread, reject->GetPromise()); + JSHandle alreadyResolved(thread, reject->GetAlreadyResolved()); + if (alreadyResolved->GetValue().IsTrue()) { + return JSTaggedValue::Undefined(); + } + alreadyResolved->SetValue(thread, JSTaggedValue::True()); + + // 6. Return RejectPromise(promise, reason). + JSHandle reason = GetCallArg(argv, 0); + JSHandle result(thread, JSPromise::RejectPromise(thread, rejectPromise, reason)); + return result.GetTaggedValue(); +} + +// es6 25.4.1.5.1 GetCapabilitiesExecutor Functions +JSTaggedValue BuiltinsPromiseHandler::Executor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, Executor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Assert: F has a [[Capability]] internal slot whose value is a PromiseCapability Record. + JSHandle executor = JSHandle::Cast(GetConstructor(argv)); + ASSERT_PRINT(executor->GetCapability().IsRecord(), + "Executor: F has a [[Capability]] internal slot whose value is a PromiseCapability Record."); + + // 2. Let promiseCapability be the value of F's [[Capability]] internal slot. + // 3. If promiseCapability.[[Resolve]] is not undefined, throw a TypeError exception. + JSHandle promiseCapability(thread, executor->GetCapability()); + if (!promiseCapability->GetResolve().IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Executor: resolve should be undefine!", JSTaggedValue::Undefined()); + } + // 4. If promiseCapability.[[Reject]] is not undefined, throw a TypeError exception. + if (!promiseCapability->GetReject().IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Executor: reject should be undefine!", JSTaggedValue::Undefined()); + } + // 5. Set promiseCapability.[[Resolve]] to resolve. + // 6. Set promiseCapability.[[Reject]] to reject. + JSHandle resolve = GetCallArg(argv, 0); + JSHandle reject = GetCallArg(argv, 1); + promiseCapability->SetResolve(thread, resolve); + promiseCapability->SetReject(thread, reject); + // 7. Return undefined. + return JSTaggedValue::Undefined(); +} + +// es6 25.4.4.1.2 Promise.all Resolve Element Functions +JSTaggedValue BuiltinsPromiseHandler::ResolveElementFunction(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, ResolveElementFunction); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle func = + JSHandle::Cast(GetConstructor(argv)); + // 1. Let alreadyCalled be the value of F's [[AlreadyCalled]] internal slot. + JSHandle alreadyCalled = + JSHandle::Cast(JSHandle(thread, func->GetAlreadyCalled())); + // 2. If alreadyCalled.[[value]] is true, return undefined. + if (alreadyCalled->GetValue().IsTrue()) { + return JSTaggedValue::Undefined(); + } + // 3. Set alreadyCalled.[[value]] to true. + alreadyCalled->SetValue(thread, JSTaggedValue::True()); + // 4. Let index be the value of F's [[Index]] internal slot. + JSHandle index(thread, func->GetIndex()); + // 5. Let values be the value of F's [[Values]] internal slot. + JSHandle values = JSHandle::Cast(JSHandle(thread, func->GetValues())); + // 6. Let promiseCapability be the value of F's [[Capabilities]] internal slot. + JSHandle capa = + JSHandle::Cast(JSHandle(thread, func->GetCapabilities())); + // 7. Let remainingElementsCount be the value of F's [[RemainingElements]] internal slot. + JSHandle remainCnt = + JSHandle::Cast(JSHandle(thread, func->GetRemainingElements())); + // 8. Set values[index] to x. + JSHandle arrayValues = + JSHandle::Cast(JSHandle(thread, values->GetValue())); + arrayValues->Set(thread, JSTaggedValue::ToUint32(thread, index), GetCallArg(argv, 0).GetTaggedValue()); + // 9. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] - 1. + remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue())); + // 10. If remainingElementsCount.[[value]] is 0, + if (remainCnt->GetValue().IsZero()) { + // a. Let valuesArray be CreateArrayFromList(values). + JSHandle jsArrayValues = JSArray::CreateArrayFromList(thread, arrayValues); + // b. Return Call(promiseCapability.[[Resolve]], undefined, «valuesArray»). + JSHandle capaResolve(thread, capa->GetResolve()); + JSHandle undefine = globalConst->GetHandledUndefined(); + JSHandle arg = factory->NewTaggedArray(1); + arg->Set(thread, 0, jsArrayValues); + return JSFunction::Call(thread, capaResolve, undefine, arg); + } + // 11. Return undefined. + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsPromiseHandler::AsyncAwaitFulfilled(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + + JSHandle value = GetCallArg(argv, 0); + JSHandle func(GetConstructor(argv)); + return JSAsyncAwaitStatusFunction::AsyncFunctionAwaitFulfilled(argv->GetThread(), func, value).GetTaggedValue(); +} + +JSTaggedValue BuiltinsPromiseHandler::AsyncAwaitRejected(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + + JSHandle reason = GetCallArg(argv, 0); + JSHandle func(GetConstructor(argv)); + return JSAsyncAwaitStatusFunction::AsyncFunctionAwaitRejected(argv->GetThread(), func, reason).GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_promise_handler.h b/ecmascript/builtins/builtins_promise_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..fc17372ac3db1490379d2afb63145d4821c8f307 --- /dev/null +++ b/ecmascript/builtins/builtins_promise_handler.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_PROMISE_HANDLER_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_PROMISE_HANDLER_H + +#include "ecmascript/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsPromiseHandler : public base::BuiltinsBase { +public: + // es6 26.6.1.3.1 Promise Resolve Functions + static JSTaggedValue Resolve(EcmaRuntimeCallInfo *argv); + + // es6 26.6.1.3.2 Promise Reject Functions + static JSTaggedValue Reject(EcmaRuntimeCallInfo *argv); + + // es6 26.6.1.5.1 GetCapabilitiesExecutor Functions + static JSTaggedValue Executor(EcmaRuntimeCallInfo *argv); + + // es2017 25.5.5.4 AsyncFunction Awaited Fulfilled + static JSTaggedValue AsyncAwaitFulfilled(EcmaRuntimeCallInfo *argv); + + // es2017 25.5.5.5 AsyncFunction Awaited Rejected + static JSTaggedValue AsyncAwaitRejected(EcmaRuntimeCallInfo *argv); + + // es6 25.4.4.1.2 Promise.all Resolve Element Functions + static JSTaggedValue ResolveElementFunction(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins + +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_PROMISE_HANDLER_H diff --git a/ecmascript/builtins/builtins_promise_job.cpp b/ecmascript/builtins/builtins_promise_job.cpp new file mode 100644 index 0000000000000000000000000000000000000000..114f41ff42e5ebacbee36e903e2b31167de7d22b --- /dev/null +++ b/ecmascript/builtins/builtins_promise_job.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_promise_job.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_tagged_value.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsPromiseJob::PromiseReactionJob(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, Reaction); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + // 1. Assert: reaction is a PromiseReaction Record. + JSHandle value = GetCallArg(argv, 0); + ASSERT(value->IsPromiseReaction()); + JSHandle reaction = JSHandle::Cast(value); + JSHandle argument = GetCallArg(argv, 1); + + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 2. Let promiseCapability be reaction.[[Capabilities]]. + JSHandle capability(thread, reaction->GetPromiseCapability()); + // 3. Let handler be reaction.[[Handler]]. + JSHandle handler(thread, reaction->GetHandler()); + JSHandle thisValue = globalConst->GetHandledUndefined(); + JSMutableHandle call(thread, capability->GetResolve()); + JSHandle result = factory->NewTaggedArray(1); + if (handler->IsString()) { + // 4. If handler is "Identity", let handlerResult be NormalCompletion(argument). + // 5. Else if handler is "Thrower", let handlerResult be Completion{[[type]]: throw, [[value]]: argument, + // [[target]]: empty}. + result->Set(thread, 0, argument); + if (EcmaString::StringsAreEqual(handler.GetObject(), + globalConst->GetHandledThrowerString().GetObject())) { + call.Update(capability->GetReject()); + } + } else { + // 6. Else, let handlerResult be Call(handler, undefined, «argument»). + JSHandle arguments = factory->NewTaggedArray(1); + arguments->Set(thread, 0, argument); + JSTaggedValue taggedValue = JSFunction::Call(thread, handler, thisValue, arguments); + result->Set(thread, 0, taggedValue); + // 7. If handlerResult is an abrupt completion, then + // a. Let status be Call(promiseCapability.[[Reject]], undefined, «handlerResult.[[value]]»). + // b. NextJob Completion(status). + if (thread->HasPendingException()) { + JSHandle throwValue = JSPromise::IfThrowGetThrowValue(thread); + thread->ClearException(); + result->Set(thread, 0, throwValue); + call.Update(capability->GetReject()); + } + } + // 8. Let status be Call(promiseCapability.[[Resolve]], undefined, «handlerResult.[[value]]»). + return JSFunction::Call(thread, call, thisValue, result); +} + +JSTaggedValue BuiltinsPromiseJob::PromiseResolveThenableJob(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, ResolveThenableJob); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle promise = GetCallArg(argv, 0); + ASSERT(promise->IsJSPromise()); + // 1. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve). + JSHandle resolvingFunctions = + JSPromise::CreateResolvingFunctions(thread, JSHandle::Cast(promise)); + JSHandle thenable = GetCallArg(argv, 1); + JSHandle then = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + + // 2. Let thenCallResult be Call(then, thenable, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»). + JSHandle array = factory->NewTaggedArray(2); + array->Set(thread, 0, resolvingFunctions->GetResolveFunction()); + array->Set(thread, 1, resolvingFunctions->GetRejectFunction()); + JSTaggedValue result = JSFunction::Call(thread, then, thenable, array); + JSHandle thenResult(thread, result); + // 3. If thenCallResult is an abrupt completion, + // a. Let status be Call(resolvingFunctions.[[Reject]], undefined, «thenCallResult.[[value]]»). + // b. NextJob Completion(status). + if (thread->HasPendingException()) { + thenResult = JSPromise::IfThrowGetThrowValue(thread); + thread->ClearException(); + JSHandle reject(thread, resolvingFunctions->GetRejectFunction()); + JSHandle argument = factory->NewTaggedArray(1); + JSHandle undefined = globalConst->GetHandledUndefined(); + argument->Set(thread, 0, thenResult); + return JSFunction::Call(thread, reject, undefined, argument); + } + // 4. NextJob Completion(thenCallResult). + return result; +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_promise_job.h b/ecmascript/builtins/builtins_promise_job.h new file mode 100644 index 0000000000000000000000000000000000000000..51f035e6ab4f3f483713074071c1663e4b51fb28 --- /dev/null +++ b/ecmascript/builtins/builtins_promise_job.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_PROMISE_JOB_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_PROMISE_JOB_H + +#include "ecmascript/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsPromiseJob : base::BuiltinsBase { +public: + static JSTaggedValue PromiseReactionJob(EcmaRuntimeCallInfo *argv); + static JSTaggedValue PromiseResolveThenableJob(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_PROMISE_JOB_H diff --git a/ecmascript/builtins/builtins_proxy.cpp b/ecmascript/builtins/builtins_proxy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9d25096b9597b4ed0709d1326b7870a4f28e444b --- /dev/null +++ b/ecmascript/builtins/builtins_proxy.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_proxy.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript::builtins { +// 26.2.1.1 Proxy( [ value ] ) +JSTaggedValue BuiltinsProxy::ProxyConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Proxy, Constructor); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + + // 1.If NewTarget is undefined, throw a TypeError exception. + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "ProxyConstructor: NewTarget is undefined", + JSTaggedValue::Exception()); + } + + // 2.Return ProxyCreate(target, handler). + JSHandle proxy = JSProxy::ProxyCreate(argv->GetThread(), GetCallArg(argv, 0), GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + return proxy.GetTaggedValue(); +} + +// 26.2.2.1 Proxy.revocable ( target, handler ) +JSTaggedValue BuiltinsProxy::Revocable([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Proxy, Revocable); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1.Let p be ProxyCreate(target, handler). + JSHandle proxy = JSProxy::ProxyCreate(thread, GetCallArg(argv, 0), GetCallArg(argv, 1)); + + // 2.ReturnIfAbrupt(p). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3 ~ 4 new revoker function and set the [[RevocableProxy]] internal slot + JSHandle revoker = thread->GetEcmaVM()->GetFactory()->NewJSProxyRevocFunction( + proxy, reinterpret_cast(InvalidateProxyFunction)); + + // 5.Let result be ObjectCreate(%ObjectPrototype%). + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle proto = env->GetObjectFunctionPrototype(); + JSHandle result = thread->GetEcmaVM()->GetFactory()->OrdinaryNewJSObjectCreate(proto); + + // 6.Perform CreateDataProperty(result, "proxy", p). + auto globalConst = thread->GlobalConstants(); + JSHandle proxyKey = globalConst->GetHandledProxyString(); + JSObject::CreateDataProperty(thread, result, proxyKey, JSHandle(proxy)); + + // 7.Perform CreateDataProperty(result, "revoke", revoker). + JSHandle revokeKey = globalConst->GetHandledRevokeString(); + JSObject::CreateDataProperty(thread, result, revokeKey, JSHandle(revoker)); + + // 8.Return result. + return result.GetTaggedValue(); +} + +// A Proxy revocation function to invalidate a specific Proxy object +JSTaggedValue BuiltinsProxy::InvalidateProxyFunction(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Proxy, InvalidateProxyFunction); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle revoke_obj(GetThis(argv)); + JSHandle revokeKey = thread->GlobalConstants()->GetHandledRevokeString(); + + PropertyDescriptor desc(thread); + JSObject::GetOwnProperty(thread, revoke_obj, revokeKey, desc); + JSProxyRevocFunction::ProxyRevocFunctions(thread, JSHandle(desc.GetValue())); + return JSTaggedValue::Undefined(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_proxy.h b/ecmascript/builtins/builtins_proxy.h new file mode 100644 index 0000000000000000000000000000000000000000..8b07160142c2986b93171e58e1c563eef4b50365 --- /dev/null +++ b/ecmascript/builtins/builtins_proxy.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_PROXY_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_PROXY_H + +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" + +namespace panda::ecmascript::builtins { +class BuiltinsProxy : public base::BuiltinsBase { +public: + // 26.2.1.1 Proxy( [ value ] ) + static JSTaggedValue ProxyConstructor(EcmaRuntimeCallInfo *argv); + + // 26.2.2.1 Proxy.revocable ( target, handler ) + static JSTaggedValue Revocable(EcmaRuntimeCallInfo *argv); + + // A Proxy revocation function to invalidate a specific Proxy object + static JSTaggedValue InvalidateProxyFunction(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_H diff --git a/ecmascript/builtins/builtins_reflect.cpp b/ecmascript/builtins/builtins_reflect.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1b3a981e5f025748c17b9637e298617ecb93fc9b --- /dev/null +++ b/ecmascript/builtins/builtins_reflect.cpp @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_reflect.h" +#include "ecmascript/js_tagged_value-inl.h" + +namespace panda::ecmascript::builtins { +// ecma 26.1.1 Reflect.apply (target, thisArgument, argumentsList) +JSTaggedValue BuiltinsReflect::ReflectApply(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, Apply); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If IsCallable(target) is false, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.apply target is not callable", JSTaggedValue::Exception()); + } + // 2. Let args be ? CreateListFromArrayLike(argumentsList). + JSHandle thisArgument = GetCallArg(argv, 1); + JSHandle argumentsList = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + JSHandle argOrAbrupt = JSObject::CreateListFromArrayLike(thread, argumentsList); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle args = JSHandle::Cast(argOrAbrupt); + + // 3. Perform PrepareForTailCall(). + // 4. Return ? Call(target, thisArgument, args). + return JSFunction::Call(thread, target, thisArgument, args); +} + +// ecma 26.1.2 Reflect.construct (target, argumentsList [ , newTarget]) +JSTaggedValue BuiltinsReflect::ReflectConstruct(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If IsConstructor(target) is false, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.construct target is not constructor", JSTaggedValue::Exception()); + } + // 2. If newTarget is not present, set newTarget to target. + JSHandle newTarget = + argv->GetArgsNumber() > 2 ? GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD) : target; // 2: num args + // 3. Else if IsConstructor(newTarget) is false, throw a TypeError exception. + if (!newTarget->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.construct newTarget is present, but not constructor", + JSTaggedValue::Exception()); + } + // 4. Let args be ? CreateListFromArrayLike(argumentsList). + JSHandle argumentsList = GetCallArg(argv, 1); + JSHandle argOrAbrupt = JSObject::CreateListFromArrayLike(thread, argumentsList); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle args = JSHandle::Cast(argOrAbrupt); + // 5. Return ? Construct(target, args, newTarget). + return JSFunction::Construct(thread, target, args, newTarget); +} + +// ecma 26.1.3 Reflect.defineProperty (target, propertyKey, attributes) +JSTaggedValue BuiltinsReflect::ReflectDefineProperty(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, DefineProperty); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.defineProperty target is not object", JSTaggedValue::Exception()); + } + // 2. Let key be ? ToPropertyKey(propertyKey). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Let desc be ? ToPropertyDescriptor(attributes). + JSHandle attributes = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + PropertyDescriptor desc(thread); + JSObject::ToPropertyDescriptor(thread, attributes, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 4. Return ? target.[[DefineOwnProperty]](key, desc). + return GetTaggedBoolean(JSTaggedValue::DefineOwnProperty(thread, target, key, desc)); +} + +// ecma 21.1.4 Reflect.deleteProperty (target, propertyKey) +JSTaggedValue BuiltinsReflect::ReflectDeleteProperty(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, DeleteProperty); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.deleteProperty target is not object", JSTaggedValue::Exception()); + } + // 2. Let key be ? ToPropertyKey(propertyKey). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return ? target.[[Delete]](key). + return GetTaggedBoolean(JSTaggedValue::DeleteProperty(thread, target, key)); +} + +// ecma 26.1.5 Reflect.get (target, propertyKey [ , receiver]) +JSTaggedValue BuiltinsReflect::ReflectGet(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, Get); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle val = GetCallArg(argv, 0); + if (!val->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.get target is not object", JSTaggedValue::Exception()); + } + JSHandle target = JSHandle::Cast(val); + // 2. Let key be ? ToPropertyKey(propertyKey). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. If receiver is not present, then + // a. Set receiver to target. + // 4. Return ? target.[[Get]](key, receiver). + if (argv->GetArgsNumber() == 2) { // 2: 2 means that there are 2 args in total + return JSObject::GetProperty(thread, target, key).GetValue().GetTaggedValue(); + } + JSHandle receiver = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + return JSObject::GetProperty(thread, val, key, receiver).GetValue().GetTaggedValue(); +} + +// ecma 26.1.6 Reflect.getOwnPropertyDescriptor ( target, propertyKey ) +JSTaggedValue BuiltinsReflect::ReflectGetOwnPropertyDescriptor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, GetOwnPropertyDescriptor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.getOwnPropertyDescriptor target is not object", + JSTaggedValue::Exception()); + } + // 2. Let key be ? ToPropertyKey(propertyKey). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Let desc be ? target.[[GetOwnProperty]](key). + PropertyDescriptor desc(thread); + if (!JSTaggedValue::GetOwnProperty(thread, target, key, desc)) { + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 4. Return FromPropertyDescriptor(desc). + JSHandle res = JSObject::FromPropertyDescriptor(thread, desc); + return res.GetTaggedValue(); +} + +// ecma 21.1.7 Reflect.getPrototypeOf (target) +JSTaggedValue BuiltinsReflect::ReflectGetPrototypeOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, GetPrototypeOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle val = GetCallArg(argv, 0); + if (!val->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.getPrototypeOf target is not object", JSTaggedValue::Exception()); + } + JSHandle target = JSHandle::Cast(val); + // 2. Return ? target.[[GetPrototypeOf]](). + return target->GetPrototype(thread); +} + +// ecma 26.1.8 Reflect.has (target, propertyKey) +JSTaggedValue BuiltinsReflect::ReflectHas(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, Has); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.has target is not object", JSTaggedValue::Exception()); + } + // 2. Let key be ? ToPropertyKey(propertyKey). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return ? target.[[HasProperty]](key). + return GetTaggedBoolean(JSTaggedValue::HasProperty(thread, target, key)); +} + +// ecma 26.1.9 Reflect.isExtensible (target) +JSTaggedValue BuiltinsReflect::ReflectIsExtensible(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.isExtensible target is not object", JSTaggedValue::Exception()); + } + // 2. Return ? target.[[IsExtensible]](). + return GetTaggedBoolean(target->IsExtensible(thread)); +} + +// ecma 26.1.10 Reflect.ownKeys (target) +JSTaggedValue BuiltinsReflect::ReflectOwnKeys(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, OwnKeys); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.ownKeys target is not object", JSTaggedValue::Exception()); + } + // 2. Let keys be ? target.[[OwnPropertyKeys]](). + JSHandle keys = JSTaggedValue::GetOwnPropertyKeys(thread, target); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return CreateArrayFromList(keys). + JSHandle result = JSArray::CreateArrayFromList(thread, keys); + return result.GetTaggedValue(); +} + +// ecma 26.1.11 Reflect.preventExtensions (target) +JSTaggedValue BuiltinsReflect::ReflectPreventExtensions(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, PreventExtensions); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.preventExtensions target is not object", + JSTaggedValue::Exception()); + } + // 2. Return ? target.[[PreventExtensions]](). + return GetTaggedBoolean(JSTaggedValue::PreventExtensions(thread, target)); +} + +// ecma 26.1.12 Reflect.set (target, propertyKey, V [ , receiver]) +JSTaggedValue BuiltinsReflect::ReflectSet(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, Set); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle targetVal = GetCallArg(argv, 0); + if (!targetVal->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.get target is not object", JSTaggedValue::Exception()); + } + // 2. Let key be ? ToPropertyKey(propertyKey). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + // 3. If receiver is not present, then + // a. Set receiver to target. + // 4. Return ? target.[[Set]](key, receiver). + if (argv->GetArgsNumber() == 3) { // 3: 3 means that there are three args in total + return GetTaggedBoolean(JSTaggedValue::SetProperty(thread, targetVal, key, value)); + } + JSHandle receiver = GetCallArg(argv, 3); // 3: 3 means the third arg + return GetTaggedBoolean(JSTaggedValue::SetProperty(thread, targetVal, key, value, receiver)); +} + +// ecma 26.1.13 Reflect.setPrototypeOf (target, proto) +JSTaggedValue BuiltinsReflect::ReflectSetPrototypeOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, SetPrototypeOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.setPrototypeOf target is not object", JSTaggedValue::Exception()); + } + // 2. If Type(proto) is not Object and proto is not null, throw a TypeError exception. + JSHandle proto = GetCallArg(argv, 1); + if (!proto->IsECMAObject() && !proto->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "SetPrototypeOf: proto is neither Object nor Null", + JSTaggedValue::Exception()); + } + // 3. Return ? target.[[SetPrototypeOf]](proto). + return GetTaggedBoolean(JSTaggedValue::SetPrototype(thread, target, proto)); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_reflect.h b/ecmascript/builtins/builtins_reflect.h new file mode 100644 index 0000000000000000000000000000000000000000..dddcb8d91945142df56abb4c26864e272c9836a0 --- /dev/null +++ b/ecmascript/builtins/builtins_reflect.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_REFLECT_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_REFLECT_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_array.h" + +namespace panda::ecmascript::builtins { +class BuiltinsReflect : public base::BuiltinsBase { +public: + // ecma 26.1.1 + static JSTaggedValue ReflectApply(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.2 + static JSTaggedValue ReflectConstruct(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.3 + static JSTaggedValue ReflectDefineProperty(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.4 + static JSTaggedValue ReflectDeleteProperty(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.5 + static JSTaggedValue ReflectGet(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.6 + static JSTaggedValue ReflectGetOwnPropertyDescriptor(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.7 + static JSTaggedValue ReflectGetPrototypeOf(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.8 + static JSTaggedValue ReflectHas(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.9 + static JSTaggedValue ReflectIsExtensible(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.10 + static JSTaggedValue ReflectOwnKeys(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.11 + static JSTaggedValue ReflectPreventExtensions(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.12 + static JSTaggedValue ReflectSet(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.13 + static JSTaggedValue ReflectSetPrototypeOf(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_REFLECT_H diff --git a/ecmascript/builtins/builtins_regexp.cpp b/ecmascript/builtins/builtins_regexp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d4b1d4b13f42c858616fe3eb08c297663b32b98c --- /dev/null +++ b/ecmascript/builtins/builtins_regexp.cpp @@ -0,0 +1,1620 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_regexp.h" +#include +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_regexp.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/mem/assert_scope-inl.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/mem/chunk_containers.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/regexp/regexp_parser_cache.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript::builtins { +// 21.2.3.1 +JSTaggedValue BuiltinsRegExp::RegExpConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle newTargetTemp = GetNewTarget(argv); + JSHandle pattern = GetCallArg(argv, 0); + JSHandle flags = GetCallArg(argv, 1); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let patternIsRegExp be IsRegExp(pattern). + bool patternIsRegExp = JSObject::IsRegExp(thread, pattern); + // 2. ReturnIfAbrupt(patternIsRegExp). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. If NewTarget is not undefined, let newTarget be NewTarget. + JSHandle newTarget; + if (!newTargetTemp->IsUndefined()) { + newTarget = newTargetTemp; + } else { + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // disable gc + [[maybe_unused]] DisallowGarbageCollection no_gc; + // 4.a Let newTarget be the active function object. + newTarget = env->GetRegExpFunction(); + JSHandle constructorString = globalConst->GetHandledConstructorString(); + // 4.b If patternIsRegExp is true and flags is undefined + if (patternIsRegExp && flags->IsUndefined()) { + // 4.b.i Let patternConstructor be Get(pattern, "constructor"). + JSHandle patternConstructor = + JSObject::GetProperty(thread, pattern, constructorString).GetValue(); + // 4.b.ii ReturnIfAbrupt(patternConstructor). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 4.b.iii If SameValue(newTarget, patternConstructor) is true, return pattern. + if (JSTaggedValue::SameValue(newTarget.GetTaggedValue(), patternConstructor.GetTaggedValue())) { + return pattern.GetTaggedValue(); + } + } + } + // 5. If Type(pattern) is Object and pattern has a [[RegExpMatcher]] internal slot + bool isJsReg = false; + if (pattern->IsECMAObject()) { + JSHandle patternObj = JSHandle::Cast(pattern); + isJsReg = patternObj->IsJSRegExp(); + } + JSHandle patternTemp; + JSHandle flagsTemp; + if (isJsReg) { + JSHandle patternReg(thread, JSRegExp::Cast(pattern->GetTaggedObject())); + // 5.a Let P be the value of pattern’s [[OriginalSource]] internal slot. + patternTemp = JSHandle(thread, patternReg->GetOriginalSource()); + if (flags->IsUndefined()) { + // 5.b If flags is undefined, let F be the value of pattern’s [[OriginalFlags]] internal slot. + flagsTemp = JSHandle(thread, patternReg->GetOriginalFlags()); + } else { + // 5.c Else, let F be flags. + flagsTemp = flags; + } + // 6. Else if patternIsRegExp is true + } else if (patternIsRegExp) { + JSHandle sourceString(factory->NewFromString("source")); + JSHandle flagsString(factory->NewFromString("flags")); + // disable gc + [[maybe_unused]] DisallowGarbageCollection noGc; + // 6.a Let P be Get(pattern, "source"). + patternTemp = JSObject::GetProperty(thread, pattern, sourceString).GetValue(); + // 6.b ReturnIfAbrupt(P). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 6.c If flags is undefined + if (flags->IsUndefined()) { + // 6.c.i Let F be Get(pattern, "flags"). + flagsTemp = JSObject::GetProperty(thread, pattern, flagsString).GetValue(); + // 6.c.ii ReturnIfAbrupt(F). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + // 6.d Else, let F be flags. + flagsTemp = flags; + } + } else { + // 7.a Let P be pattern. + patternTemp = pattern; + // 7.b Let F be flags. + flagsTemp = flags; + } + // 8. Let O be RegExpAlloc(newTarget). + JSHandle object(thread, RegExpAlloc(thread, newTarget)); + // 9. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 10. Return RegExpInitialize(O, P, F). + JSTaggedValue result = RegExpInitialize(thread, object, patternTemp, flagsTemp); + return JSTaggedValue(result); +} + +// prototype +// 20.2.5.2 +JSTaggedValue BuiltinsRegExp::Exec(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Exec); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let R be the this value. + JSHandle thisObj = GetThis(argv); + // 4. Let S be ToString(string). + JSHandle inputStr = GetCallArg(argv, 0); + JSHandle stringHandle = JSTaggedValue::ToString(thread, inputStr); + // 5. ReturnIfAbrupt(S). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle string = JSHandle::Cast(stringHandle); + // 2. If Type(R) is not Object, throw a TypeError exception. + if (!thisObj->IsECMAObject()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + // 3. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception. + if (!thisObj->IsJSRegExp()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[RegExpMatcher]]", JSTaggedValue::Exception()); + } + // 6. Return RegExpBuiltinExec(R, S). + JSTaggedValue result = RegExpBuiltinExec(thread, thisObj, string); + return JSTaggedValue(result); +} + +// 20.2.5.13 +JSTaggedValue BuiltinsRegExp::Test(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Test); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let R be the this value. + JSHandle thisObj = GetThis(argv); + JSHandle inputStr = GetCallArg(argv, 0); + // 3. Let string be ToString(S). + // 4. ReturnIfAbrupt(string). + JSHandle stringHandle = JSTaggedValue::ToString(thread, inputStr); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle string = JSHandle::Cast(stringHandle); + // 2. If Type(R) is not Object, throw a TypeError exception. + if (!thisObj->IsECMAObject()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + + // 5. Let match be RegExpExec(R, string). + JSTaggedValue matchResult = RegExpExec(thread, thisObj, string); + // 6. ReturnIfAbrupt(match). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. If match is not null, return true; else return false. + return GetTaggedBoolean(!matchResult.IsNull()); +} + +// 20.2.5.14 +JSTaggedValue BuiltinsRegExp::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let R be the this value. + JSHandle thisObj = GetThis(argv); + auto ecmaVm = thread->GetEcmaVM(); + // 2. If Type(R) is not Object, throw a TypeError exception. + if (!thisObj->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle sourceString(factory->NewFromString("source")); + JSHandle flagsString(factory->NewFromString("flags")); + // 3. Let pattern be ToString(Get(R, "source")). + JSHandle getSource(JSObject::GetProperty(thread, thisObj, sourceString).GetValue()); + JSHandle getFlags(JSObject::GetProperty(thread, thisObj, flagsString).GetValue()); + JSHandle sourceStrHandle = JSTaggedValue::ToString(thread, getSource); + // 4. ReturnIfAbrupt(pattern). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. Let flags be ToString(Get(R, "flags")). + JSHandle flagsStrHandle = JSTaggedValue::ToString(thread, getFlags); + // 4. ReturnIfAbrupt(flags). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle slashStr = factory->NewFromString("/"); + // 7. Let result be the String value formed by concatenating "/", pattern, and "/", and flags. + JSHandle tempStr = factory->ConcatFromString(slashStr, sourceStrHandle); + JSHandle resultTemp = factory->ConcatFromString(tempStr, slashStr); + return factory->ConcatFromString(resultTemp, flagsStrHandle).GetTaggedValue(); +} + +// 20.2.5.3 +JSTaggedValue BuiltinsRegExp::GetFlags(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, GetFlags); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let R be the this value. + JSHandle thisObj = GetThis(argv); + // 2. If Type(R) is not Object, throw a TypeError exception. + if (!thisObj->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + // 3. Let result be the empty String. + // 4. ~ 19. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle result(factory->GetEmptyString()); + result = ConcatFlags(thread, thisObj, result, "global"); + result = ConcatFlags(thread, thisObj, result, "ignoreCase"); + result = ConcatFlags(thread, thisObj, result, "multiline"); + result = ConcatFlags(thread, thisObj, result, "dotAll"); + result = ConcatFlags(thread, thisObj, result, "unicode"); + result = ConcatFlags(thread, thisObj, result, "sticky"); + return JSTaggedValue(static_cast(result->GetTaggedObject())); +} + +// 20.2.5.4 +JSTaggedValue BuiltinsRegExp::GetGlobal(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + + JSHandle gString = thread->GetEcmaVM()->GetFactory()->NewFromString("g"); + bool result = GetFlagsInternal(thread, thisObj, gString); + return GetTaggedBoolean(result); +} + +// 20.2.5.5 +JSTaggedValue BuiltinsRegExp::GetIgnoreCase(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + JSHandle iString = thread->GetEcmaVM()->GetFactory()->NewFromString("i"); + bool result = GetFlagsInternal(thread, thisObj, iString); + return GetTaggedBoolean(result); +} + +// 20.2.5.7 +JSTaggedValue BuiltinsRegExp::GetMultiline(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + JSHandle mString = thread->GetEcmaVM()->GetFactory()->NewFromString("m"); + bool result = GetFlagsInternal(thread, thisObj, mString); + return GetTaggedBoolean(result); +} + +JSTaggedValue BuiltinsRegExp::GetDotAll(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + JSHandle sString = thread->GetEcmaVM()->GetFactory()->NewFromString("s"); + bool result = GetFlagsInternal(thread, thisObj, sString); + return GetTaggedBoolean(result); +} + +// 20.2.5.10 +JSTaggedValue BuiltinsRegExp::GetSource(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let R be the this value. + JSHandle thisObj = GetThis(argv); + // 2. If Type(R) is not Object, throw a TypeError exception. + // 3. If R does not have an [[OriginalSource]] internal slot, throw a TypeError exception. + // 4. If R does not have an [[OriginalFlags]] internal slot, throw a TypeError exception. + if (!thisObj->IsECMAObject()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + if (!thisObj->IsJSRegExp()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[OriginalSource]]", JSTaggedValue::Exception()); + } + // 5. Let src be the value of R’s [[OriginalSource]] internal slot. + JSHandle regexpObj(thread, JSRegExp::Cast(thisObj->GetTaggedObject())); + JSHandle source(thread, regexpObj->GetOriginalSource()); + // 6. Let flags be the value of R’s [[OriginalFlags]] internal slot. + JSHandle flags(thread, regexpObj->GetOriginalFlags()); + // 7. Return EscapeRegExpPattern(src, flags). + return JSTaggedValue(EscapeRegExpPattern(thread, source, flags)); +} + +// 20.2.5.12 +JSTaggedValue BuiltinsRegExp::GetSticky(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + JSHandle yString = thread->GetEcmaVM()->GetFactory()->NewFromString("y"); + bool result = GetFlagsInternal(thread, thisObj, yString); + return GetTaggedBoolean(result); +} + +// 20.2.5.15 +JSTaggedValue BuiltinsRegExp::GetUnicode(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + JSHandle uString = thread->GetEcmaVM()->GetFactory()->NewFromString("u"); + bool result = GetFlagsInternal(thread, thisObj, uString); + return GetTaggedBoolean(result); +} + +// 21.2.4.2 +JSTaggedValue BuiltinsRegExp::GetSpecies(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetThis(argv).GetTaggedValue(); +} + +// 21.2.5.6 +JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Match); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let rx be the this value. + JSHandle thisObj = GetThis(argv); + // 3. Let S be ToString(string) + JSHandle inputString = GetCallArg(argv, 0); + JSHandle stringHandle = JSTaggedValue::ToString(thread, inputString); + // 4. ReturnIfAbrupt(string). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle string = JSHandle::Cast(stringHandle); + if (!thisObj->IsECMAObject()) { + // 2. If Type(rx) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + // 5. Let global be ToBoolean(Get(rx, "global")). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle global(factory->NewFromString("global")); + auto globalValue = JSObject::GetProperty(thread, thisObj, global).GetValue(); + // 6. ReturnIfAbrupt(global). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool isGlobal = globalValue->ToBoolean(); + // 7. If global is false, then + if (!isGlobal) { + // a. Return RegExpExec(rx, S). + JSTaggedValue result = RegExpExec(thread, thisObj, string); + return JSTaggedValue(result); + } + // 8. Else global is true + // a. Let fullUnicode be ToBoolean(Get(rx, "unicode")). + JSHandle unicode(factory->NewFromString("unicode")); + JSHandle unicodeHandle = JSObject::GetProperty(thread, thisObj, unicode).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool fullUnicode = unicodeHandle->ToBoolean(); + // b. ReturnIfAbrupt(fullUnicode) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. Let setStatus be Set(rx, "lastIndex", 0, true). + JSHandle lastIndexString(thread->GlobalConstants()->GetHandledLastIndexString()); + JSHandle value(thread, JSTaggedValue(0)); + JSObject::SetProperty(thread, thisObj, lastIndexString, value, true); + // d. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // e. Let A be ArrayCreate(0). + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + // f. Let n be 0. + int resultNum = 0; + JSMutableHandle result(thread, JSTaggedValue(0)); + // g. Repeat, + while (true) { + // i. Let result be RegExpExec(rx, S). + result.Update(RegExpExec(thread, thisObj, string)); + // ii. ReturnIfAbrupt(result). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // iii. If result is null, then + if (result->IsNull()) { + // 1. If n=0, return null. + if (resultNum == 0) { + return JSTaggedValue::Null(); + } + // 2. Else, return A. + return array.GetTaggedValue(); + } + // iv. Else result is not null, + // 1. Let matchStr be ToString(Get(result, "0")). + JSHandle zoreString(factory->NewFromString("0")); + JSHandle matchStr(JSObject::GetProperty(thread, result, zoreString).GetValue()); + JSHandle matchString = JSTaggedValue::ToString(thread, matchStr); + // 2. ReturnIfAbrupt(matchStr). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle matchValue = JSHandle::Cast(matchString); + // 3. Let status be CreateDataProperty(A, ToString(n), matchStr). + JSObject::CreateDataProperty(thread, array, resultNum, matchValue); + // 5. If matchStr is the empty String, then + if (JSTaggedValue::ToString(thread, matchValue)->GetLength() == 0) { + // a. Let thisIndex be ToLength(Get(rx, "lastIndex")). + JSHandle lastIndexHandle = + JSObject::GetProperty(thread, thisObj, lastIndexString).GetValue(); + JSTaggedNumber thisIndex = JSTaggedValue::ToLength(thread, lastIndexHandle); + // b. ReturnIfAbrupt(thisIndex). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode). + // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true). + JSHandle nextIndex( + thread, JSTaggedValue(AdvanceStringIndex(thread, string, thisIndex.GetNumber(), fullUnicode))); + JSObject::SetProperty(thread, thisObj, lastIndexString, nextIndex, true); + // e. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 6. Increment n. + resultNum++; + } +} + +JSTaggedValue BuiltinsRegExp::RegExpReplaceFast(JSThread *thread, JSHandle ®exp, + JSHandle inputString, uint32_t inputLength) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // get bytecode + JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer(); + void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer(); + // get flags + auto bytecodeBuffer = reinterpret_cast(dynBuf); + uint32_t flags = *reinterpret_cast(bytecodeBuffer + RegExpParser::FLAGS_OFFSET); + JSHandle lastIndexHandle(thread->GlobalConstants()->GetHandledLastIndexString()); + uint32_t lastIndex; + JSHandle regexpHandle(regexp); + + if ((flags & (RegExpParser::FLAG_STICKY | RegExpParser::FLAG_GLOBAL)) == 0) { + lastIndex = 0; + } else { + JSHandle thisIndexHandle = JSObject::GetProperty(thread, regexp, lastIndexHandle).GetValue(); + lastIndex = JSTaggedValue::ToLength(thread, thisIndexHandle).GetNumber(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + JSHandle tagInputString = JSHandle::Cast(inputString); + JSHandle pattern(thread, regexpHandle->GetOriginalSource()); + JSHandle flag(thread, regexpHandle->GetOriginalFlags()); + JSHandle cacheTable(thread->GetEcmaVM()->GetRegExpCache()); + if (lastIndex == 0 && inputLength > MIN_REPLACE_STRING_LENGTH) { + JSTaggedValue cacheResult = + cacheTable->FindCachedResult(thread, pattern, flag, tagInputString, RegExpExecResultCache::REPLACE_TYPE); + if (cacheResult != JSTaggedValue::Undefined()) { + return cacheResult; + } + } + + std::string resultString; + uint32_t nextPosition = 0; + JSMutableHandle lastIndexValue(thread, JSTaggedValue(lastIndex)); + + // 12. Let done be false. + // 13. Repeat, while done is false + for (;;) { + if (lastIndex > inputLength) { + break; + } + + bool isUtf16 = inputString->IsUtf16(); + const uint8_t *strBuffer; + CVector u8Buffer; + CVector u16Buffer; + if (isUtf16) { + u16Buffer = CVector(inputLength); + inputString->CopyDataUtf16(u16Buffer.data(), inputLength); + strBuffer = reinterpret_cast(u16Buffer.data()); + } else { + u8Buffer = CVector(inputLength + 1); + inputString->CopyDataUtf8(u8Buffer.data(), inputLength + 1); + strBuffer = u8Buffer.data(); + } + + RegExpExecutor::MatchResult matchResult = Matcher(thread, regexp, strBuffer, inputLength, lastIndex, isUtf16); + if (!matchResult.isSuccess_) { + if (flags & (RegExpParser::FLAG_STICKY | RegExpParser::FLAG_GLOBAL)) { + lastIndex = 0; + lastIndexValue.Update(JSTaggedValue(lastIndex)); + JSObject::SetProperty(thread, regexp, lastIndexHandle, lastIndexValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + break; + } + uint32_t startIndex = matchResult.index_; + uint32_t endIndex = matchResult.endIndex_; + lastIndex = endIndex; + if (nextPosition < startIndex) { + resultString += base::StringHelper::SubString(thread, inputString, nextPosition, startIndex - nextPosition); + } + nextPosition = endIndex; + if (!(flags & RegExpParser::FLAG_GLOBAL)) { + // a. Let setStatus be Set(R, "lastIndex", e, true). + lastIndexValue.Update(JSTaggedValue(lastIndex)); + JSObject::SetProperty(thread, regexp, lastIndexHandle, lastIndexValue, true); + // b. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + break; + } + if (endIndex == startIndex) { + bool unicode = inputString->IsUtf16() && (flags & RegExpParser::FLAG_UTF16); + endIndex = AdvanceStringIndex(thread, tagInputString, endIndex, unicode); + } + lastIndex = endIndex; + } + resultString += base::StringHelper::SubString(thread, inputString, nextPosition, inputLength - nextPosition); + JSTaggedValue resultValue = factory->NewFromStdString(resultString).GetTaggedValue(); + if (lastIndex == 0 && inputLength > MIN_REPLACE_STRING_LENGTH) { + cacheTable->AddResultInCache(thread, pattern, flag, tagInputString, resultValue, + RegExpExecResultCache::REPLACE_TYPE); + } + return resultValue; +} + +// 21.2.5.8 +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Replace); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let rx be the this value. + JSHandle thisObj = GetThis(argv); + if (!thisObj->IsECMAObject()) { + // 2. If Type(rx) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + // 3. Let S be ToString(string). + JSHandle string = GetCallArg(argv, 0); + JSHandle inputReplaceValue = GetCallArg(argv, 1); + JSHandle srcString = JSTaggedValue::ToString(thread, string); + + // 4. ReturnIfAbrupt(S). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle inputStr = JSHandle::Cast(srcString); + // 5. Let lengthS be the number of code unit elements in S. + uint32_t length = static_cast(inputStr->GetTaggedObject())->GetLength(); + // 6. Let functionalReplace be IsCallable(replaceValue). + bool functionalReplace = inputReplaceValue->IsCallable(); + JSHandle replaceValueHandle; + if (!functionalReplace) { + replaceValueHandle = JSTaggedValue::ToString(thread, inputReplaceValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + JSHandle lastIndex(thread->GlobalConstants()->GetHandledLastIndexString()); + // 8. Let global be ToBoolean(Get(rx, "global")). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle global(factory->NewFromString("global")); + auto globalValue = JSObject::GetProperty(thread, thisObj, global).GetValue(); + // 9. ReturnIfAbrupt(global). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool isGlobal = globalValue->ToBoolean(); + // 10. If global is true, then + bool fullUnicode = false; + if (isGlobal) { + // a. Let fullUnicode be ToBoolean(Get(rx, "unicode")). + JSHandle unicode(factory->NewFromString("unicode")); + JSHandle fullUnicodeHandle = JSObject::GetProperty(thread, thisObj, unicode).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + fullUnicode = fullUnicodeHandle->ToBoolean(); + // b. ReturnIfAbrupt(fullUnicode). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. Let setStatus be Set(rx, "lastIndex", 0, true). + JSHandle lastIndexValue(thread, JSTaggedValue(0)); + JSObject::SetProperty(thread, thisObj, lastIndex, lastIndexValue, true); + // d. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + if (isGlobal && !functionalReplace && (replaceValueHandle->GetLength() == 0) && thisObj->IsJSRegExp()) { + JSHClass *hclass = JSHandle::Cast(thisObj)->GetJSHClass(); + JSHClass *originHClass = JSHClass::Cast(thread->GlobalConstants()->GetJSRegExpClass().GetTaggedObject()); + if (hclass == originHClass) { + return RegExpReplaceFast(thread, thisObj, srcString, length); + } + } + + JSHandle matchedStr(factory->NewFromString("0")); + // 11. Let results be a new empty List. + JSHandle resultsList(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + int resultsIndex = 0; + // 12. Let done be false. + // 13. Repeat, while done is false + JSMutableHandle nextIndexHandle(thread, JSTaggedValue(0)); + for (;;) { + // a. Let result be RegExpExec(rx, S). + JSHandle execResult(thread, RegExpExec(thread, thisObj, inputStr)); + // b. ReturnIfAbrupt(result). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. If result is null, set done to true. + if (execResult->IsNull()) { + break; + } + // d. Else result is not null, i. Append result to the end of results. + JSObject::CreateDataProperty(thread, resultsList, resultsIndex, execResult); + resultsIndex++; + // ii. If global is false, set done to true. + if (!isGlobal) { + break; + } + // iii. Else, 1. Let matchStr be ToString(Get(result, "0")). + JSHandle getMatch = JSObject::GetProperty(thread, execResult, matchedStr).GetValue(); + JSHandle matchString = JSTaggedValue::ToString(thread, getMatch); + // 2. ReturnIfAbrupt(matchStr). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. If matchStr is the empty String, then + if (matchString->GetLength() == 0) { + // a. Let thisIndex be ToLength(Get(rx, "lastIndex")). + JSHandle thisIndexHandle = JSObject::GetProperty(thread, thisObj, lastIndex).GetValue(); + uint32_t thisIndex = JSTaggedValue::ToLength(thread, thisIndexHandle).GetNumber(); + // b. ReturnIfAbrupt(thisIndex). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode). + uint32_t nextIndex = AdvanceStringIndex(thread, inputStr, thisIndex, fullUnicode); + nextIndexHandle.Update(JSTaggedValue(nextIndex)); + // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true). + JSObject::SetProperty(thread, thisObj, lastIndex, nextIndexHandle, true); + // e. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + // 14. Let accumulatedResult be the empty String value. + std::string accumulatedResult; + // 15. Let nextSourcePosition be 0. + uint32_t nextSourcePosition = 0; + JSHandle getMatchString; + // 16. Repeat, for each result in results, + for (int i = 0; i < resultsIndex; i++) { + JSHandle resultValues = + JSObject::GetProperty(thread, JSHandle(resultsList), i).GetValue(); + // a. Let nCaptures be ToLength(Get(result, "length")). + JSHandle lengthHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle ncapturesHandle = JSObject::GetProperty(thread, resultValues, lengthHandle).GetValue(); + uint32_t ncaptures = JSTaggedValue::ToUint32(thread, ncapturesHandle); + // b. ReturnIfAbrupt(nCaptures). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. Let nCaptures be max(nCaptures − 1, 0). + ncaptures = std::max((ncaptures - 1), 0); + // d. Let matched be ToString(Get(result, "0")). + getMatchString = JSObject::GetProperty(thread, resultValues, matchedStr).GetValue(); + JSHandle matchString = JSTaggedValue::ToString(thread, getMatchString); + // e. ReturnIfAbrupt(matched). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // f. Let matchLength be the number of code units in matched. + uint32_t matchLength = matchString->GetLength(); + // g. Let position be ToInteger(Get(result, "index")). + JSHandle resultIndex(factory->NewFromString("index")); + JSHandle positionHandle = JSObject::GetProperty(thread, resultValues, resultIndex).GetValue(); + uint32_t position = JSTaggedValue::ToUint32(thread, positionHandle); + // h. ReturnIfAbrupt(position). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // i. Let position be max(min(position, lengthS), 0). + position = std::max(std::min(position, length), 0); + // j. Let n be 1. + uint32_t index = 1; + // k. Let captures be an empty List. + JSHandle capturesList = factory->NewTaggedArray(ncaptures); + // l. Repeat while n ≤ nCaptures + while (index <= ncaptures) { + // i. Let capN be Get(result, ToString(n)). + JSHandle capN = JSObject::GetProperty(thread, resultValues, index).GetValue(); + // ii. ReturnIfAbrupt(capN). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // iii. If capN is not undefined, then + if (!capN->IsUndefined()) { + // 1. Let capN be ToString(capN). + JSHandle capNStr = JSTaggedValue::ToString(thread, capN); + // 2. ReturnIfAbrupt(capN). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle capnStr = JSHandle::Cast(capNStr); + capturesList->Set(thread, index - 1, capnStr); + } else { + // iv. Append capN as the last element of captures. + capturesList->Set(thread, index - 1, capN); + } + // v. Let n be n+1 + ++index; + } + // m. If functionalReplace is true, then + CString replacement; + JSHandle replacerArgs = + factory->NewTaggedArray(3 + capturesList->GetLength()); // 3: «matched, pos, and string» + if (functionalReplace) { + // i. Let replacerArgs be «matched». + replacerArgs->Set(thread, 0, getMatchString.GetTaggedValue()); + // ii. Append in list order the elements of captures to the end of the List replacerArgs. + // iii. Append position and S as the last two elements of replacerArgs. + index = 0; + while (index < capturesList->GetLength()) { + replacerArgs->Set(thread, index + 1, capturesList->Get(index)); + ++index; + } + replacerArgs->Set(thread, index + 1, JSTaggedValue(position)); + replacerArgs->Set(thread, index + 2, inputStr.GetTaggedValue()); // 2: position of string + // iv. Let replValue be Call(replaceValue, undefined, replacerArgs). + JSHandle undefined(thread, JSTaggedValue::Undefined()); + JSTaggedValue replaceResult = JSFunction::Call(thread, inputReplaceValue, undefined, replacerArgs); + JSHandle replValue(thread, replaceResult); + // v. Let replacement be ToString(replValue). + JSHandle replacementString = JSTaggedValue::ToString(thread, replValue); + // o. ReturnIfAbrupt(replacement). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + replacement = ConvertToString(*replacementString, StringConvertedUsage::LOGICOPERATION); + } else { + // n. Else, + JSHandle replacementHandle( + thread, BuiltinsString::GetSubstitution(thread, matchString, srcString, position, capturesList, + replaceValueHandle)); + replacement = ConvertToString(EcmaString::Cast(replacementHandle->GetTaggedObject()), + StringConvertedUsage::LOGICOPERATION); + } + // p. If position ≥ nextSourcePosition, then + if (position >= nextSourcePosition) { + // ii. Let accumulatedResult be the String formed by concatenating the code units of the current value + // of accumulatedResult with the substring of S consisting of the code units from nextSourcePosition + // (inclusive) up to position (exclusive) and with the code units of replacement. + accumulatedResult += base::StringHelper::SubString(thread, JSHandle::Cast(inputStr), + nextSourcePosition, (position - nextSourcePosition)); + accumulatedResult += replacement; + // iii. Let nextSourcePosition be position + matchLength. + nextSourcePosition = position + matchLength; + } + } + // 17. If nextSourcePosition ≥ lengthS, return accumulatedResult. + if (nextSourcePosition >= length) { + return factory->NewFromStdString(accumulatedResult).GetTaggedValue(); + } + // 18. Return the String formed by concatenating the code units of accumulatedResult with the substring of S + // consisting of the code units from nextSourcePosition (inclusive) up through the final code unit of S(inclusive). + accumulatedResult += base::StringHelper::SubString(thread, JSHandle::Cast(inputStr), nextSourcePosition, + (length - nextSourcePosition)); + return factory->NewFromStdString(accumulatedResult).GetTaggedValue(); +} + +// 21.2.5.9 +JSTaggedValue BuiltinsRegExp::Search(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Search); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let rx be the this value. + JSHandle thisObj = GetThis(argv); + // 3. Let S be ToString(string). + JSHandle inputStr = GetCallArg(argv, 0); + JSHandle stringHandle = JSTaggedValue::ToString(thread, inputStr); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle string = JSHandle::Cast(stringHandle); + if (!thisObj->IsECMAObject()) { + // 2. If Type(rx) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + // 4. Let previousLastIndex be ? Get(rx, "lastIndex"). + JSHandle lastIndexString(thread->GlobalConstants()->GetHandledLastIndexString()); + JSHandle previousLastIndex = JSObject::GetProperty(thread, thisObj, lastIndexString).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. If SameValue(previousLastIndex, 0) is false, then + // Perform ? Set(rx, "lastIndex", 0, true). + if (!JSTaggedValue::SameValue(previousLastIndex.GetTaggedValue(), JSTaggedValue(0))) { + JSHandle value(thread, JSTaggedValue(0)); + JSObject::SetProperty(thread, thisObj, lastIndexString, value, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 6. Let result be ? RegExpExec(rx, S). + JSHandle result(thread, RegExpExec(thread, thisObj, string)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. Let currentLastIndex be ? Get(rx, "lastIndex"). + JSHandle currentLastIndex = JSObject::GetProperty(thread, thisObj, lastIndexString).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 8. If SameValue(currentLastIndex, previousLastIndex) is false, then + // Perform ? Set(rx, "lastIndex", previousLastIndex, true). + if (!JSTaggedValue::SameValue(previousLastIndex.GetTaggedValue(), currentLastIndex.GetTaggedValue())) { + JSObject::SetProperty(thread, thisObj, lastIndexString, previousLastIndex, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 9. If result is null, return -1. + if (result->IsNull()) { + return JSTaggedValue(-1); + } + // 10. Return ? Get(result, "index"). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle index(factory->NewFromString("index")); + return JSObject::GetProperty(thread, result, index).GetValue().GetTaggedValue(); +} + +// 21.2.5.11 +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Split); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let rx be the this value. + JSHandle thisObj = GetThis(argv); + auto ecmaVm = thread->GetEcmaVM(); + // 3. Let S be ToString(string). + JSHandle inputString = GetCallArg(argv, 0); + JSHandle limit = GetCallArg(argv, 1); + JSHandle stringHandle = JSTaggedValue::ToString(thread, inputString); + + // 4. ReturnIfAbrupt(string). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle jsString = JSHandle::Cast(stringHandle); + if (!thisObj->IsECMAObject()) { + // 2. If Type(rx) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + // 5. Let C be SpeciesConstructor(rx, %RegExp%). + JSHandle defaultConstructor = ecmaVm->GetGlobalEnv()->GetRegExpFunction(); + JSHandle objHandle(thisObj); + JSHandle constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor); + // 6. ReturnIfAbrupt(C). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. Let flags be ToString(Get(rx, "flags")). + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle flagsString(factory->NewFromString("flags")); + JSHandle taggedFlags = JSObject::GetProperty(thread, thisObj, flagsString).GetValue(); + JSHandle flags; + + if (taggedFlags->IsUndefined()) { + flags = factory->GetEmptyString(); + } else { + flags = JSTaggedValue::ToString(thread, taggedFlags); + } + // 8. ReturnIfAbrupt(flags). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 9. If flags contains "u", let unicodeMatching be true. + // 10. Else, let unicodeMatching be false. + JSHandle uStringHandle(factory->NewFromString("u")); + bool unicodeMatching = base::StringHelper::Contains(*flags, *uStringHandle); + // 11. If flags contains "y", let newFlags be flags. + JSHandle newFlagsHandle; + JSHandle yStringHandle(factory->NewFromString("y")); + if (base::StringHelper::Contains(*flags, *yStringHandle)) { + newFlagsHandle = flags; + } else { + // 12. Else, let newFlags be the string that is the concatenation of flags and "y". + JSHandle yStr = factory->NewFromString("y"); + newFlagsHandle = factory->ConcatFromString(flags, yStr); + } + + // 17. If limit is undefined, let lim be 2^32–1; else let lim be ToUint32(limit). + uint32_t lim; + if (limit->IsUndefined()) { + lim = MAX_SPLIT_LIMIT; + } else { + lim = JSTaggedValue::ToUint32(thread, limit); + // 18. ReturnIfAbrupt(lim). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + JSHandle regexpHandle(thisObj); + JSHandle pattern(thread, regexpHandle->GetOriginalSource()); + JSHandle flag(thread, regexpHandle->GetOriginalFlags()); + JSHandle cacheTable(thread->GetEcmaVM()->GetRegExpCache()); + if (lim == MAX_SPLIT_LIMIT) { + JSTaggedValue cacheResult = + cacheTable->FindCachedResult(thread, pattern, flag, inputString, RegExpExecResultCache::SPLIT_TYPE); + if (cacheResult != JSTaggedValue::Undefined()) { + return cacheResult; + } + } + + // 13. Let splitter be Construct(C, «rx, newFlags»). + JSHandle globalObject(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()); + JSHandle undefined(thread, JSTaggedValue::Undefined()); + JSHandle arguments = factory->NewTaggedArray(2); // 2: «rx, newFlags» + arguments->Set(thread, 0, thisObj.GetTaggedValue()); + arguments->Set(thread, 1, newFlagsHandle.GetTaggedValue()); + JSTaggedValue taggedSplitter = JSFunction::Construct(thread, constructor, arguments, undefined); + // 14. ReturnIfAbrupt(splitter). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle splitter(thread, taggedSplitter); + // 15. Let A be ArrayCreate(0). + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + // 16. Let lengthA be 0. + uint32_t aLength = 0; + + // 19. Let size be the number of elements in S. + uint32_t size = static_cast(jsString->GetTaggedObject())->GetLength(); + // 20. Let p be 0. + uint32_t startIndex = 0; + // 21. If lim = 0, return A. + if (lim == 0) { + return JSTaggedValue(static_cast(array.GetTaggedValue().GetTaggedObject())); + } + // 22. If size = 0, then + if (size == 0) { + // a. Let z be RegExpExec(splitter, S). + JSHandle execResult(thread, RegExpExec(thread, splitter, jsString)); + // b. ReturnIfAbrupt(z). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. If z is not null, return A. + if (!execResult->IsNull()) { + return JSTaggedValue(static_cast(array.GetTaggedValue().GetTaggedObject())); + } + // d. Assert: The following call will never result in an abrupt completion. + // e. Perform CreateDataProperty(A, "0", S). + JSObject::CreateDataProperty(thread, array, 0, jsString); + // f. Return A. + return JSTaggedValue(static_cast(array.GetTaggedValue().GetTaggedObject())); + } + // 23. Let q be p. + uint32_t endIndex = startIndex; + JSMutableHandle lastIndexvalue(thread, JSTaggedValue(endIndex)); + // 24. Repeat, while q < size + JSHandle lastIndexString(thread->GlobalConstants()->GetHandledLastIndexString()); + while (endIndex < size) { + // a. Let setStatus be Set(splitter, "lastIndex", q, true). + lastIndexvalue.Update(JSTaggedValue(endIndex)); + JSObject::SetProperty(thread, splitter, lastIndexString, lastIndexvalue, true); + // b. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle execResult(thread, RegExpExec(thread, splitter, jsString)); + // d. ReturnIfAbrupt(z). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // e. If z is null, let q be AdvanceStringIndex(S, q, unicodeMatching). + if (execResult->IsNull()) { + endIndex = AdvanceStringIndex(thread, jsString, endIndex, unicodeMatching); + } else { + // f. Else z is not null, + // i. Let e be ToLength(Get(splitter, "lastIndex")). + JSHandle lastIndexHandle = + JSObject::GetProperty(thread, splitter, lastIndexString).GetValue(); + JSTaggedNumber lastIndexNumber = JSTaggedValue::ToLength(thread, lastIndexHandle); + // ii. ReturnIfAbrupt(e). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + uint32_t lastIndex = lastIndexNumber.GetNumber(); + // iii. If e = p, let q be AdvanceStringIndex(S, q, unicodeMatching). + if (lastIndex == startIndex) { + endIndex = AdvanceStringIndex(thread, jsString, endIndex, unicodeMatching); + } else { + // iv. Else e != p, + // 1. Let T be a String value equal to the substring of S consisting of the elements at indices p + // (inclusive) through q (exclusive). + std::string stdStrT = base::StringHelper::SubString(thread, JSHandle::Cast(jsString), + startIndex, (endIndex - startIndex)); + // 2. Assert: The following call will never result in an abrupt completion. + // 3. Perform CreateDataProperty(A, ToString(lengthA), T). + JSHandle tValue(factory->NewFromStdString(stdStrT)); + JSObject::CreateDataProperty(thread, array, aLength, tValue); + // 4. Let lengthA be lengthA +1. + ++aLength; + // 5. If lengthA = lim, return A. + if (aLength == lim) { + if (lim == MAX_SPLIT_LIMIT) { + cacheTable->AddResultInCache(thread, pattern, flag, inputString, array.GetTaggedValue(), + RegExpExecResultCache::SPLIT_TYPE); + } + return array.GetTaggedValue(); + } + // 6. Let p be e. + startIndex = lastIndex; + // 7. Let numberOfCaptures be ToLength(Get(z, "length")). + JSHandle lengthString(factory->NewFromString("length")); + JSHandle capturesHandle = + JSObject::GetProperty(thread, execResult, lengthString).GetValue(); + JSTaggedNumber numberOfCapturesNumber = JSTaggedValue::ToLength(thread, capturesHandle); + // 8. ReturnIfAbrupt(numberOfCaptures). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + uint32_t numberOfCaptures = numberOfCapturesNumber.GetNumber(); + // 9. Let numberOfCaptures be max(numberOfCaptures-1, 0). + numberOfCaptures = (numberOfCaptures == 0) ? 0 : numberOfCaptures - 1; + // 10. Let i be 1. + uint32_t i = 1; + // 11. Repeat, while i ≤ numberOfCaptures. + while (i <= numberOfCaptures) { + // a. Let nextCapture be Get(z, ToString(i)). + JSHandle nextCapture = JSObject::GetProperty(thread, execResult, i).GetValue(); + // b. ReturnIfAbrupt(nextCapture). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. Perform CreateDataProperty(A, ToString(lengthA), nextCapture). + JSObject::CreateDataProperty(thread, array, aLength, nextCapture); + // d. Let i be i + 1. + ++i; + // e. Let lengthA be lengthA +1. + ++aLength; + // f. If lengthA = lim, return A. + if (aLength == lim) { + if (lim == MAX_SPLIT_LIMIT) { + cacheTable->AddResultInCache(thread, pattern, flag, inputString, array.GetTaggedValue(), + RegExpExecResultCache::SPLIT_TYPE); + } + return array.GetTaggedValue(); + } + } + // 12. Let q be p. + endIndex = startIndex; + } + } + } + // 25. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive) + // through size (exclusive). + std::string stdStrT = + base::StringHelper::SubString(thread, JSHandle::Cast(jsString), startIndex, (size - startIndex)); + // 26. Assert: The following call will never result in an abrupt completion. + // 27. Perform CreateDataProperty(A, ToString(lengthA), t). + JSHandle tValue(factory->NewFromStdString(stdStrT)); + JSObject::CreateDataProperty(thread, array, aLength, tValue); + if (lim == MAX_SPLIT_LIMIT) { + cacheTable->AddResultInCache(thread, pattern, flag, inputString, array.GetTaggedValue(), + RegExpExecResultCache::SPLIT_TYPE); + } + // 28. Return A. + return array.GetTaggedValue(); +} + +// NOLINTNEXTLINE(readability-non-const-parameter) +RegExpExecutor::MatchResult BuiltinsRegExp::Matcher(JSThread *thread, const JSHandle ®exp, + const uint8_t *buffer, size_t length, int32_t lastIndex, + bool isUtf16) +{ + // get bytecode + JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer(); + void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer(); + auto bytecodeBuffer = reinterpret_cast(dynBuf); + // execute + Chunk chunk(thread->GetRegionFactory()); + RegExpExecutor executor(&chunk); + if (lastIndex < 0) { + lastIndex = 0; + } + bool ret = executor.Execute(buffer, lastIndex, length, bytecodeBuffer, isUtf16); + RegExpExecutor::MatchResult result = executor.GetResult(thread, ret); + return result; +} + +uint32_t BuiltinsRegExp::AdvanceStringIndex(JSThread *thread, const JSHandle &inputStr, uint32_t index, + bool unicode) +{ + // 1. Assert: Type(S) is String. + ASSERT(inputStr->IsString()); + // 2. Assert: index is an integer such that 0≤index≤2^53 - 1 + ASSERT(index <= pow(2, 53) - 1); + // 3. Assert: Type(unicode) is Boolean. + // 4. If unicode is false, return index+1. + if (!unicode) { + return index + 1; + } + // 5. Let length be the number of code units in S. + uint32_t length = static_cast(inputStr->GetTaggedObject())->GetLength(); + // 6. If index+1 ≥ length, return index+1. + if (index + 1 >= length) { + return index + 1; + } + // 7. Let first be the code unit value at index index in S. + uint16_t first = static_cast(inputStr->GetTaggedObject())->At(index); + // 8. If first < 0xD800 or first > 0xDFFF, return index+1. + if (first < 0xD800 || first > 0xDFFF) { // NOLINT(readability-magic-numbers) + return index + 1; + } + // 9. Let second be the code unit value at index index+1 in S. + uint16_t second = static_cast(inputStr->GetTaggedObject())->At(index + 1); + // 10. If second < 0xDC00 or second > 0xDFFF, return index+1. + if (second < 0xDC00 || second > 0xDFFF) { // NOLINT(readability-magic-numbers) + return index + 1; + } + // 11. Return index + 2. + return index + 2; +} + +JSHandle BuiltinsRegExp::ConcatFlags(JSThread *thread, const JSHandle &obj, + const JSHandle &string, const char *name) +{ + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle nameString(factory->NewFromString(name)); + bool exist = JSObject::GetProperty(thread, obj, nameString).GetValue()->ToBoolean(); + // ReturnIfAbrupt + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + if (exist) { + JSHandle temp = factory->GetEmptyString(); + if (CString("global") == name) { + temp = factory->NewFromString("g"); + } else if (CString("ignoreCase") == name) { + temp = factory->NewFromString("i"); + } else if (CString("multiline") == name) { + temp = factory->NewFromString("m"); + } else if (CString("dotAll") == name) { + temp = factory->NewFromString("s"); + } else if (CString("unicode") == name) { + temp = factory->NewFromString("u"); + } else if (CString("sticky") == name) { + temp = factory->NewFromString("y"); + } + JSHandle thisString(string); + return JSHandle(factory->ConcatFromString(thisString, temp)); + } + return JSHandle(string); +} + +bool BuiltinsRegExp::GetFlagsInternal(JSThread *thread, const JSHandle &obj, + const JSHandle &flag) +{ + // 1. Let R be the this value. + // 2. If Type(R) is not Object, throw a TypeError exception. + if (!obj->IsECMAObject()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", false); + } + // 3. If R does not have an [[OriginalFlags]] internal slot, throw a TypeError exception. + JSHandle patternObj = JSHandle::Cast(obj); + if (!patternObj->IsJSRegExp()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[OriginalFlags]]", false); + } + // 4. Let flags be the value of R’s [[OriginalFlags]] internal slot. + JSHandle regexpObj(thread, JSRegExp::Cast(obj->GetTaggedObject())); + // 5. If flags contains the code unit "[flag]", return true. + // 6. Return false. + auto flagsStr = static_cast(regexpObj->GetOriginalFlags().GetTaggedObject()); + return base::StringHelper::Contains(flagsStr, *flag); +} + +// 21.2.5.2.2 +JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle ®exp, + const JSHandle &inputStr) +{ + ASSERT(JSObject::IsRegExp(thread, regexp)); + ASSERT(inputStr->IsString()); + int32_t length = static_cast(inputStr->GetTaggedObject())->GetLength(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle lastIndexHandle(thread->GlobalConstants()->GetHandledLastIndexString()); + JSHandle lastIndexResult = JSObject::GetProperty(thread, regexp, lastIndexHandle).GetValue(); + JSTaggedNumber lastIndexNumber = JSTaggedValue::ToLength(thread, lastIndexResult); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t lastIndex = lastIndexNumber.GetNumber(); + JSHandle globalHandle(factory->NewFromString("global")); + bool global = JSObject::GetProperty(thread, regexp, globalHandle).GetValue()->ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle stickyHandle(factory->NewFromString("sticky")); + bool sticky = JSObject::GetProperty(thread, regexp, stickyHandle).GetValue()->ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!global && !sticky) { + lastIndex = 0; + } + JSHandle regexpObj(thread, JSRegExp::Cast(regexp->GetTaggedObject())); + auto flagsStr = static_cast(regexpObj->GetOriginalFlags().GetTaggedObject()); + JSHandle uString = factory->NewFromString("u"); + [[maybe_unused]] bool fullUnicode = base::StringHelper::Contains(flagsStr, *uString); + if (lastIndex > length) { + JSHandle lastIndexValue(thread, JSTaggedValue(0)); + JSObject::SetProperty(thread, regexp, lastIndexHandle, lastIndexValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::Null(); + } + JSHandle inputString = JSTaggedValue::ToString(thread, inputStr); + bool isUtf16 = inputString->IsUtf16(); + const uint8_t *strBuffer; + size_t stringLength = inputString->GetLength(); + CVector u8Buffer; + CVector u16Buffer; + if (isUtf16) { + u16Buffer = CVector(stringLength); + inputString->CopyDataUtf16(u16Buffer.data(), stringLength); + strBuffer = reinterpret_cast(u16Buffer.data()); + } else { + u8Buffer = CVector(stringLength + 1); + inputString->CopyDataUtf8(u8Buffer.data(), stringLength + 1); + strBuffer = u8Buffer.data(); + } + RegExpExecutor::MatchResult matchResult = Matcher(thread, regexp, strBuffer, stringLength, lastIndex, isUtf16); + if (!matchResult.isSuccess_) { + if (global || sticky) { + JSHandle lastIndexValue(thread, JSTaggedValue(0)); + JSObject::SetProperty(thread, regexp, lastIndexHandle, lastIndexValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + return JSTaggedValue::Null(); + } + uint32_t endIndex = matchResult.endIndex_; + if (global || sticky) { + // a. Let setStatus be Set(R, "lastIndex", e, true). + JSHandle lastIndexValue(thread, JSTaggedValue(endIndex)); + JSObject::SetProperty(thread, regexp, lastIndexHandle, lastIndexValue, true); + // b. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + uint32_t capturesSize = matchResult.captures_.size(); + JSHandle results(JSArray::ArrayCreate(thread, JSTaggedNumber(capturesSize))); + uint32_t matchIndex = matchResult.index_; + // 24. Perform CreateDataProperty(A, "index", matchIndex). + JSHandle indexKey(factory->NewFromString("index")); + JSHandle indexValue(thread, JSTaggedValue(matchIndex)); + JSObject::CreateDataProperty(thread, results, indexKey, indexValue); + // 25. Perform CreateDataProperty(A, "input", S). + JSHandle inputKey(factory->NewFromString("input")); + + JSHandle inputValue(thread, static_cast(inputStr->GetTaggedObject())); + JSObject::CreateDataProperty(thread, results, inputKey, inputValue); + // 27. Perform CreateDataProperty(A, "0", matched_substr). + JSHandle zeroValue(matchResult.captures_[0].second); + JSObject::CreateDataProperty(thread, results, 0, zeroValue); + // 28. For each integer i such that i > 0 and i <= n + for (uint32_t i = 1; i < capturesSize; i++) { + // a. Let capture_i be ith element of r's captures List + JSTaggedValue capturedValue; + if (matchResult.captures_[i].first) { + capturedValue = JSTaggedValue::Undefined(); + } else { + capturedValue = matchResult.captures_[i].second.GetTaggedValue(); + } + JSHandle iValue(thread, capturedValue); + JSObject::CreateDataProperty(thread, results, i, iValue); + } + // 29. Return A. + return results.GetTaggedValue(); +} + +// 21.2.5.2.1 +JSTaggedValue BuiltinsRegExp::RegExpExec(JSThread *thread, const JSHandle ®exp, + const JSHandle &inputString) +{ + // 1. Assert: Type(R) is Object. + ASSERT(regexp->IsECMAObject()); + // 2. Assert: Type(S) is String. + ASSERT(inputString->IsString()); + // 3. Let exec be Get(R, "exec"). + JSHandle thisObj(thread, regexp->GetTaggedObject()); + JSHandle inputStr = JSTaggedValue::ToString(thread, inputString); + + JSHandle execHandle(thread->GlobalConstants()->GetHandledExecString()); + JSHandle exec = + JSObject::GetProperty(thread, JSHandle(thisObj), execHandle).GetValue(); + // 4. ReturnIfAbrupt(exec). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. If IsCallable(exec) is true, then + if (exec->IsCallable()) { + JSHClass *hclass = JSHandle::Cast(regexp)->GetJSHClass(); + JSHClass *originHClass = JSHClass::Cast(thread->GlobalConstants()->GetJSRegExpClass().GetTaggedObject()); + if (hclass == originHClass) { + // 7. Return RegExpBuiltinExec(R, S). + return RegExpBuiltinExec(thread, regexp, inputString); + } + JSHandle obj = JSHandle::Cast(thisObj); + JSHandle arguments = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1); + arguments->Set(thread, 0, inputStr.GetTaggedValue()); + JSTaggedValue result = JSFunction::Call(thread, exec, obj, arguments); + // b. ReturnIfAbrupt(result). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!result.IsECMAObject() && !result.IsNull()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "exec result is null or is not Object", JSTaggedValue::Exception()); + } + return result; + } + // 6. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception. + if (!thisObj->IsJSRegExp()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have a [[RegExpMatcher]]", JSTaggedValue::Exception()); + } + // 7. Return RegExpBuiltinExec(R, S). + return RegExpBuiltinExec(thread, regexp, inputString); +} + +// 21.2.3.2.1 +JSTaggedValue BuiltinsRegExp::RegExpAlloc(JSThread *thread, const JSHandle &newTarget) +{ + /** + * 1. Let obj be OrdinaryCreateFromConstructor(newTarget, "%RegExpPrototype%", + * «[[RegExpMatcher]],[[OriginalSource]], [[OriginalFlags]]»). + * */ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle func = env->GetRegExpFunction(); + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(func), newTarget)); + // 2. ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. Return obj. + return obj.GetTaggedValue(); +} + +uint32_t BuiltinsRegExp::UpdateExpressionFlags(JSThread *thread, const CString &checkStr) +{ + uint32_t flagsBits = 0; + uint32_t flagsBitsTemp = 0; + for (char i : checkStr) { + switch (i) { + case 'g': + flagsBitsTemp = RegExpParser::FLAG_GLOBAL; + break; + case 'i': + flagsBitsTemp = RegExpParser::FLAG_IGNORECASE; + break; + case 'm': + flagsBitsTemp = RegExpParser::FLAG_MULTILINE; + break; + case 's': + flagsBitsTemp = RegExpParser::FLAG_DOTALL; + break; + case 'u': + flagsBitsTemp = RegExpParser::FLAG_UTF16; + break; + case 'y': + flagsBitsTemp = RegExpParser::FLAG_STICKY; + break; + default: { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle syntaxError = + factory->GetJSError(base::ErrorType::SYNTAX_ERROR, "invalid regular expression flags"); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), 0); + } + } + if ((flagsBits & flagsBitsTemp) != 0) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle syntaxError = + factory->GetJSError(base::ErrorType::SYNTAX_ERROR, "invalid regular expression flags"); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), 0); + } + flagsBits |= flagsBitsTemp; + } + return flagsBits; +} + +// 21.2.3.2.2 +JSTaggedValue BuiltinsRegExp::RegExpInitialize(JSThread *thread, const JSHandle &obj, + const JSHandle &pattern, + const JSHandle &flags) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle patternStrHandle; + JSHandle flagsStrHandle; + // 1. If pattern is undefined, let P be the empty String. + if (pattern->IsUndefined()) { + patternStrHandle = factory->GetEmptyString(); + } else { + // 2. Else, let P be ToString(pattern). + patternStrHandle = JSTaggedValue::ToString(thread, pattern); + // 3. ReturnIfAbrupt(P). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 4. If flags is undefined, let F be the empty String. + if (flags->IsUndefined()) { + flagsStrHandle = factory->GetEmptyString(); + } else { + // 5. Else, let F be ToString(flags). + flagsStrHandle = JSTaggedValue::ToString(thread, flags); + // 6. ReturnIfAbrupt(F). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + /** + * 7. If F contains any code unit other than "g", "i", "m", "u", or "y" or if it contains the same code + * unit more than once, throw a SyntaxError exception. + **/ + CString checkStr = ConvertToString(*flagsStrHandle, StringConvertedUsage::LOGICOPERATION); + uint32_t flagsBits = UpdateExpressionFlags(thread, checkStr); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // String -> CString + CString patternStdStr = ConvertToString(*patternStrHandle, StringConvertedUsage::LOGICOPERATION); + // 9. 10. + Chunk chunk(thread->GetRegionFactory()); + RegExpParser parser = RegExpParser(&chunk); + RegExpParserCache *regExpParserCache = thread->GetEcmaVM()->GetRegExpParserCache(); + auto getCache = regExpParserCache->GetCache(*patternStrHandle, flagsBits); + if (getCache.first == JSTaggedValue::Hole()) { + parser.Init(const_cast(reinterpret_cast(patternStdStr.c_str())), patternStdStr.size(), + flagsBits); + parser.Parse(); + if (parser.IsError()) { + JSHandle syntaxError = + factory->GetJSError(base::ErrorType::SYNTAX_ERROR, parser.GetErrorMsg().c_str()); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), JSTaggedValue::Exception()); + } + } + JSHandle regexp(thread, JSRegExp::Cast(obj->GetTaggedObject())); + // 11. Set the value of obj’s [[OriginalSource]] internal slot to P. + regexp->SetOriginalSource(thread, patternStrHandle.GetTaggedValue()); + // 12. Set the value of obj’s [[OriginalFlags]] internal slot to F. + regexp->SetOriginalFlags(thread, flagsStrHandle.GetTaggedValue()); + // 13. Set obj’s [[RegExpMatcher]] internal slot. + if (getCache.first == JSTaggedValue::Hole()) { + auto bufferSize = parser.GetOriginBufferSize(); + auto buffer = parser.GetOriginBuffer(); + factory->NewJSRegExpByteCodeData(regexp, buffer, bufferSize); + regExpParserCache->SetCache(*patternStrHandle, flagsBits, regexp->GetByteCodeBuffer(), bufferSize); + } else { + regexp->SetByteCodeBuffer(thread, getCache.first); + regexp->SetLength(thread, JSTaggedValue(static_cast(getCache.second))); + } + // 14. Let setStatus be Set(obj, "lastIndex", 0, true). + JSHandle lastIndexString(thread->GlobalConstants()->GetHandledLastIndexString()); + JSHandle value(thread, JSTaggedValue(0)); + JSObject::SetProperty(thread, obj, lastIndexString, value, true); + // 15. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 16. Return obj. + return obj.GetTaggedValue(); +} + +JSTaggedValue BuiltinsRegExp::RegExpCreate(JSThread *thread, const JSHandle &pattern, + const JSHandle &flags) +{ + BUILTINS_API_TRACE(thread, RegExp, Create); + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + JSHandle newTarget = env->GetRegExpFunction(); + // 1. Let obj be RegExpAlloc(%RegExp%). + JSHandle object(thread, RegExpAlloc(thread, newTarget)); + // 2. ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return RegExpInitialize(obj, P, F). + return RegExpInitialize(thread, object, pattern, flags); +} + +// 21.2.3.2.4 +EcmaString *BuiltinsRegExp::EscapeRegExpPattern(JSThread *thread, const JSHandle &src, + const JSHandle &flag) +{ + // String -> CString + JSHandle srcStr(thread, static_cast(src->GetTaggedObject())); + JSHandle flagStr(thread, static_cast(flag->GetTaggedObject())); + CString srcStdStr = ConvertToString(*srcStr, StringConvertedUsage::LOGICOPERATION); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // "" -> (?:) + if (srcStdStr.empty()) { + srcStdStr = "(?:)"; + } + // "/" -> "\/" + srcStdStr = base::StringHelper::RepalceAll(srcStdStr, "/", "\\/"); + // "\\" -> "\" + srcStdStr = base::StringHelper::RepalceAll(srcStdStr, "\\", "\\"); + + return *factory->NewFromString(srcStdStr); +} + +JSTaggedValue RegExpExecResultCache::CreateCacheTable(JSThread *thread) +{ + int length = CACHE_TABLE_HEADER_SIZE + DEFAULT_CACHE_NUMBER * ENTRY_SIZE; + + auto table = static_cast(*thread->GetEcmaVM()->GetFactory()->NewDictionaryArray(length)); + table->SetHitCount(thread, 0); + table->SetCacheCount(thread, 0); + + return JSTaggedValue(table); +} + +JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread, const JSHandle &pattern, + const JSHandle &flag, + const JSHandle &input, CacheType type) +{ + JSHandle patternStr(pattern); + JSHandle flagStr(flag); + JSHandle inputStr(input); + JSTaggedValue patternValue = pattern.GetTaggedValue(); + JSTaggedValue flagValue = flag.GetTaggedValue(); + JSTaggedValue inputValue = input.GetTaggedValue(); + + if (!pattern->IsString() || !flag->IsString() || !input->IsString()) { + return JSTaggedValue::Undefined(); + } + + uint32_t hash = pattern->GetKeyHashCode() + flag->GetKeyHashCode() + input->GetKeyHashCode(); + uint32_t entry = hash & (RegExpExecResultCache::DEFAULT_CACHE_NUMBER - 1); + if (!Match(entry, patternValue, flagValue, inputValue)) { + uint32_t entry2 = (entry + 1) & (DEFAULT_CACHE_NUMBER - 1); + if (!Match(entry2, patternValue, flagValue, inputValue)) { + return JSTaggedValue::Undefined(); + } + entry = entry2; + } + uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + JSTaggedValue result; + switch (type) { + case REPLACE_TYPE: + result = Get(index + RESULT_REPLACE_INDEX); + break; + case SPLIT_TYPE: + result = Get(index + RESULT_SPLIT_INDEX); + break; + default: + UNREACHABLE(); + break; + } + SetHitCount(thread, GetHitCount() + 1); + return result; +} + +void RegExpExecResultCache::AddResultInCache(JSThread *thread, const JSHandle &pattern, + const JSHandle &flag, const JSHandle &input, + JSTaggedValue resultArray, CacheType type) +{ + JSHandle patternStr(pattern); + JSHandle flagStr(flag); + JSHandle inputStr(input); + + if (!pattern->IsString() || !flag->IsString() || !input->IsString()) { + return; + } + + JSTaggedValue patternValue = pattern.GetTaggedValue(); + JSTaggedValue flagValue = flag.GetTaggedValue(); + JSTaggedValue inputValue = input.GetTaggedValue(); + + uint32_t hash = pattern->GetKeyHashCode() + flag->GetKeyHashCode() + input->GetKeyHashCode(); + uint32_t entry = hash & (DEFAULT_CACHE_NUMBER - 1); + uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + if (Get(index) == JSTaggedValue::Undefined()) { + SetCacheCount(thread, GetCacheCount() + 1); + SetEntry(thread, entry, patternValue, flagValue, inputValue); + UpdateResultArray(thread, entry, resultArray, type); + } else if (Match(entry, patternValue, flagValue, inputValue)) { + UpdateResultArray(thread, entry, resultArray, type); + } else { + uint32_t entry2 = (entry + 1) & (DEFAULT_CACHE_NUMBER - 1); + uint32_t index2 = CACHE_TABLE_HEADER_SIZE + entry2 * ENTRY_SIZE; + if (Get(index2) == JSTaggedValue::Undefined()) { + SetCacheCount(thread, GetCacheCount() + 1); + SetEntry(thread, entry2, patternValue, flagValue, inputValue); + UpdateResultArray(thread, entry2, resultArray, type); + } else if (Match(entry2, patternValue, flagValue, inputValue)) { + UpdateResultArray(thread, entry2, resultArray, type); + } else { + SetCacheCount(thread, GetCacheCount() - 1); + ClearEntry(thread, entry2); + SetEntry(thread, entry, patternValue, flagValue, inputValue); + UpdateResultArray(thread, entry, resultArray, type); + } + } +} + +void RegExpExecResultCache::SetEntry(JSThread *thread, int entry, JSTaggedValue &pattern, JSTaggedValue &flag, + JSTaggedValue &input) +{ + int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + Set(thread, index + PATTERN_INDEX, pattern); + Set(thread, index + FLAG_INDEX, flag); + Set(thread, index + INPUT_STRING_INDEX, input); +} + +void RegExpExecResultCache::UpdateResultArray(JSThread *thread, int entry, JSTaggedValue resultArray, CacheType type) +{ + int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + switch (type) { + break; + case REPLACE_TYPE: + Set(thread, index + RESULT_REPLACE_INDEX, resultArray); + break; + case SPLIT_TYPE: + Set(thread, index + RESULT_SPLIT_INDEX, resultArray); + break; + default: + UNREACHABLE(); + break; + } +} + +void RegExpExecResultCache::ClearEntry(JSThread *thread, int entry) +{ + int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + JSTaggedValue undefined = JSTaggedValue::Undefined(); + for (int i = 0; i < ENTRY_SIZE; i++) { + Set(thread, index + i, undefined); + } +} +bool RegExpExecResultCache::Match(int entry, JSTaggedValue &pattern, JSTaggedValue &flag, JSTaggedValue &input) +{ + int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + JSTaggedValue keyPattern = Get(index + PATTERN_INDEX); + JSTaggedValue keyFlag = Get(index + FLAG_INDEX); + JSTaggedValue keyInput = Get(index + INPUT_STRING_INDEX); + + if (keyPattern == JSTaggedValue::Undefined()) { + return false; + } + + EcmaString *patternStr = EcmaString::Cast(pattern.GetTaggedObject()); + EcmaString *flagStr = EcmaString::Cast(flag.GetTaggedObject()); + EcmaString *inputStr = EcmaString::Cast(input.GetTaggedObject()); + EcmaString *keyPatternStr = EcmaString::Cast(keyPattern.GetTaggedObject()); + EcmaString *keyFlagStr = EcmaString::Cast(keyFlag.GetTaggedObject()); + EcmaString *keyInputStr = EcmaString::Cast(keyInput.GetTaggedObject()); + return EcmaString::StringsAreEqual(patternStr, keyPatternStr) && EcmaString::StringsAreEqual(flagStr, keyFlagStr) && + EcmaString::StringsAreEqual(inputStr, keyInputStr); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_regexp.h b/ecmascript/builtins/builtins_regexp.h new file mode 100644 index 0000000000000000000000000000000000000000..50ce7911bb169802561e11c6b75ea661b33c8931 --- /dev/null +++ b/ecmascript/builtins/builtins_regexp.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_REGEXP_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_REGEXP_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/builtins/builtins_string.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/regexp/regexp_executor.h" +#include "ecmascript/regexp/regexp_parser.h" + +namespace panda::ecmascript::builtins { +class BuiltinsRegExp : public base::BuiltinsBase { +public: + // 21.2.3.1 RegExp ( pattern, flags ) + static JSTaggedValue RegExpConstructor(EcmaRuntimeCallInfo *argv); + + // prototype + // 21.2.5.2 RegExp.prototype.exec ( string ) + static JSTaggedValue Exec(EcmaRuntimeCallInfo *argv); + // 21.2.5.13 RegExp.prototype.test( S ) + static JSTaggedValue Test(EcmaRuntimeCallInfo *argv); + // 21.2.5.14 RegExp.prototype.toString ( ) + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 21.2.5.3 get RegExp.prototype.flags + static JSTaggedValue GetFlags(EcmaRuntimeCallInfo *argv); + // 21.2.5.4 get RegExp.prototype.global + static JSTaggedValue GetGlobal(EcmaRuntimeCallInfo *argv); + // 21.2.5.5 get RegExp.prototype.ignoreCase + static JSTaggedValue GetIgnoreCase(EcmaRuntimeCallInfo *argv); + // 21.2.5.7 get RegExp.prototype.multiline + static JSTaggedValue GetMultiline(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetDotAll(EcmaRuntimeCallInfo *argv); + // 21.2.5.10 get RegExp.prototype.source + static JSTaggedValue GetSource(EcmaRuntimeCallInfo *argv); + // 21.2.5.12 get RegExp.prototype.sticky + static JSTaggedValue GetSticky(EcmaRuntimeCallInfo *argv); + // 21.2.5.15 get RegExp.prototype.unicode + static JSTaggedValue GetUnicode(EcmaRuntimeCallInfo *argv); + // 21.2.4.2 get RegExp [ @@species ] + static JSTaggedValue GetSpecies(EcmaRuntimeCallInfo *argv); + // 21.2.5.6 RegExp.prototype [ @@match ] ( string ) + static JSTaggedValue Match(EcmaRuntimeCallInfo *argv); + // 21.2.5.8 RegExp.prototype [ @@replace ] ( string, replaceValue ) + static JSTaggedValue Replace(EcmaRuntimeCallInfo *argv); + // 21.2.5.9 RegExp.prototype [ @@search ] ( string ) + static JSTaggedValue Search(EcmaRuntimeCallInfo *argv); + // 21.2.5.11 RegExp.prototype [ @@split ] ( string, limit ) + static JSTaggedValue Split(EcmaRuntimeCallInfo *argv); + // 21.2.3.2.3 Runtime Semantics: RegExpCreate ( P, F ) + static JSTaggedValue RegExpCreate(JSThread *thread, const JSHandle &pattern, + const JSHandle &flags); + +private: + static constexpr uint32_t MIN_REPLACE_STRING_LENGTH = 1000; + static constexpr uint32_t MAX_SPLIT_LIMIT = 0xFFFFFFFFu; + + static RegExpExecutor::MatchResult Matcher(JSThread *thread, const JSHandle ®exp, + const uint8_t *buffer, size_t length, int32_t lastindex, bool isUtf16); + // 21.2.5.2.3 AdvanceStringIndex ( S, index, unicode ) + static uint32_t AdvanceStringIndex(JSThread *thread, const JSHandle &inputStr, uint32_t index, + bool unicode); + static JSHandle ConcatFlags(JSThread *thread, const JSHandle &obj, + const JSHandle &string, const char *name); + + static bool GetFlagsInternal(JSThread *thread, const JSHandle &obj, + const JSHandle &flag); + // 21.2.5.2.2 Runtime Semantics: RegExpBuiltinExec ( R, S ) + static JSTaggedValue RegExpBuiltinExec(JSThread *thread, const JSHandle ®exp, + const JSHandle &inputStr); + // 21.2.5.2.1 Runtime Semantics: RegExpExec ( R, S ) + static JSTaggedValue RegExpExec(JSThread *thread, const JSHandle ®exp, + const JSHandle &inputString); + // 21.2.3.2.1 Runtime Semantics: RegExpAlloc ( newTarget ) + static JSTaggedValue RegExpAlloc(JSThread *thread, const JSHandle &newTarget); + + static uint32_t UpdateExpressionFlags(JSThread *thread, const CString &checkStr); + + // 21.2.3.2.2 Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) + static JSTaggedValue RegExpInitialize(JSThread *thread, const JSHandle &obj, + const JSHandle &pattern, const JSHandle &flags); + // 21.2.3.2.4 Runtime Semantics: EscapeRegExpPattern ( P, F ) + static EcmaString *EscapeRegExpPattern(JSThread *thread, const JSHandle &src, + const JSHandle &flag); + static JSTaggedValue RegExpReplaceFast(JSThread *thread, JSHandle ®exp, + JSHandle inputString, uint32_t inputLength); +}; + +class RegExpExecResultCache : public TaggedArray { +public: + enum CacheType { + REPLACE_TYPE, + SPLIT_TYPE, + }; + static RegExpExecResultCache *Cast(TaggedObject *object) + { + return reinterpret_cast(object); + } + static JSTaggedValue CreateCacheTable(JSThread *thread); + JSTaggedValue FindCachedResult(JSThread *thread, const JSHandle &patten, + const JSHandle &flag, const JSHandle &input, + CacheType type); + void AddResultInCache(JSThread *thread, const JSHandle &patten, const JSHandle &flag, + const JSHandle &input, JSTaggedValue resultArray, CacheType type); + + void ClearEntry(JSThread *thread, int entry); + void SetEntry(JSThread *thread, int entry, JSTaggedValue &patten, JSTaggedValue &flag, JSTaggedValue &input); + void UpdateResultArray(JSThread *thread, int entry, JSTaggedValue resultArray, CacheType type); + bool Match(int entry, JSTaggedValue &pattenStr, JSTaggedValue &flagStr, JSTaggedValue &inputStr); + inline void SetHitCount(JSThread *thread, int hitCount) + { + Set(thread, CACHE_HIT_COUNT_INDEX, JSTaggedValue(hitCount)); + } + + inline int GetHitCount() + { + return Get(CACHE_HIT_COUNT_INDEX).GetInt(); + } + + inline void SetCacheCount(JSThread *thread, int hitCount) + { + Set(thread, CACHE_COUNT_INDEX, JSTaggedValue(hitCount)); + } + + inline int GetCacheCount() + { + return Get(CACHE_COUNT_INDEX).GetInt(); + } + + void Print() + { + std::cout << "cache count: " << GetCacheCount() << std::endl; + std::cout << "cache hit count: " << GetHitCount() << std::endl; + } + +private: + static constexpr int DEFAULT_CACHE_NUMBER = 0x1000; + static constexpr int CACHE_COUNT_INDEX = 0; + static constexpr int CACHE_HIT_COUNT_INDEX = 1; + static constexpr int CACHE_TABLE_HEADER_SIZE = 2; + static constexpr int PATTERN_INDEX = 0; + static constexpr int FLAG_INDEX = 1; + static constexpr int INPUT_STRING_INDEX = 2; + static constexpr int RESULT_REPLACE_INDEX = 3; + static constexpr int RESULT_SPLIT_INDEX = 4; + static constexpr int ENTRY_SIZE = 5; +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_REGEXP_H diff --git a/ecmascript/builtins/builtins_set.cpp b/ecmascript/builtins/builtins_set.cpp new file mode 100644 index 0000000000000000000000000000000000000000..58e0aacc0ce5ecce2106970e575ec4514321809f --- /dev/null +++ b/ecmascript/builtins/builtins_set.cpp @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "builtins_set.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/linked_hash_table-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsSet::SetConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1.If NewTarget is undefined, throw a TypeError exception + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + // throw type error + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + // 2.Let set be OrdinaryCreateFromConstructor(NewTarget, "%SetPrototype%", «‍[[SetData]]» ). + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + // 3.returnIfAbrupt() + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle set = JSHandle::Cast(obj); + // 3.ReturnIfAbrupt(set). + // 4.Set set’s [[SetData]] internal slot to a new empty List. + JSTaggedValue linkedSet = LinkedHashSet::Create(thread); + set->SetLinkedSet(thread, linkedSet); + + // add data into set from iterable + // 5.If iterable is not present, let iterable be undefined. + // 6.If iterable is either undefined or null, let iter be undefined. + JSHandle iterable(GetCallArg(argv, 0)); + // 8.If iter is undefined, return set + if (iterable->IsUndefined() || iterable->IsNull()) { + return set.GetTaggedValue(); + } + // Let adder be Get(set, "add"). + JSHandle adderKey(factory->NewFromString("add")); + JSHandle setHandle(set); + JSHandle adder = JSObject::GetProperty(thread, setHandle, adderKey).GetValue(); + // ReturnIfAbrupt(adder). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue()); + // If IsCallable(adder) is false, throw a TypeError exception + if (!adder->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue()); + } + // Let iter be GetIterator(iterable). + JSHandle iter(JSIterator::GetIterator(thread, iterable)); + // ReturnIfAbrupt(iter). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue()); + // values in iterator_result may be a JSArray, values[0] = key values[1]=value, used valueIndex to get value from + // jsarray + JSHandle valueIndex(thread, JSTaggedValue(1)); + JSHandle next = JSIterator::IteratorStep(thread, iter); + while (!next->IsFalse()) { + // ReturnIfAbrupt(next). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + // Let nextValue be IteratorValue(next). + JSHandle nextValue(JSIterator::IteratorValue(thread, next)); + // ReturnIfAbrupt(nextValue). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, nextValue.GetTaggedValue()); + JSHandle array(factory->NewTaggedArray(1)); + array->Set(thread, 0, nextValue); + if (nextValue->IsArray(thread)) { + auto prop = JSObject::GetProperty(thread, nextValue, valueIndex).GetValue(); + array->Set(thread, 0, prop); + } + JSTaggedValue ret = JSFunction::Call(thread, adder, JSHandle(set), array); + // Let status be Call(adder, set, «nextValue.[[value]]»). + JSHandle status(thread, ret); + + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, status); + } + // Let next be IteratorStep(iter). + next = JSIterator::IteratorStep(thread, iter); + } + return set.GetTaggedValue(); +} + +JSTaggedValue BuiltinsSet::Add(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Add); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); + } + + JSHandle value(GetCallArg(argv, 0)); + JSHandle set(thread, JSSet::Cast(*JSTaggedValue::ToObject(thread, self))); + + JSSet::Add(thread, set, value); + return set.GetTaggedValue(); +} + +JSTaggedValue BuiltinsSet::Clear(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Clear); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); + } + JSHandle set(thread, JSSet::Cast(*JSTaggedValue::ToObject(thread, self))); + JSSet::Clear(thread, set); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsSet::Delete(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Delete); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); + } + + JSHandle set(thread, JSSet::Cast(*JSTaggedValue::ToObject(thread, self))); + JSHandle value = GetCallArg(argv, 0); + bool flag = JSSet::Delete(thread, set, value); + return GetTaggedBoolean(flag); +} + +JSTaggedValue BuiltinsSet::Has(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Has); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); + } + JSSet *jsSet = JSSet::Cast(*JSTaggedValue::ToObject(thread, self)); + JSHandle value = GetCallArg(argv, 0); + bool flag = jsSet->Has(value.GetTaggedValue()); + return GetTaggedBoolean(flag); +} + +JSTaggedValue BuiltinsSet::ForEach([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); + } + JSHandle set(thread, JSSet::Cast(*JSTaggedValue::ToObject(thread, self))); + + // 4.If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle func(GetCallArg(argv, 0)); + if (!func->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "callbackfn is not callable", JSTaggedValue::Exception()); + } + + // 5.If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArg = GetCallArg(argv, 1); + + // composed arguments + int argumentsLength = 3; + JSHandle array(factory->NewTaggedArray(argumentsLength)); + JSHandle iter(factory->NewJSSetIterator(set, IterationKind::KEY)); + JSHandle result = JSIterator::IteratorStep(thread, iter); + while (!result->IsFalse()) { + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result.GetTaggedValue()); + JSHandle value = JSIterator::IteratorValue(thread, result); + array->Set(thread, 0, value); + array->Set(thread, 1, value); + array->Set(thread, 2, set); // 2: the third value is set + // Let funcResult be Call(callbackfn, T, «e, e, S»). + JSTaggedValue ret = JSFunction::Call(thread, func, thisArg, array); + // returnIfAbrupt + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret); + result = JSIterator::IteratorStep(thread, iter); + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsSet::Species([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return GetThis(argv).GetTaggedValue(); +} + +JSTaggedValue BuiltinsSet::GetSize(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, GetSize); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self(GetThis(argv)); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); + } + JSSet *jsSet = JSSet::Cast(*JSTaggedValue::ToObject(thread, self)); + int count = jsSet->GetSize(); + return JSTaggedValue(count); +} + +JSTaggedValue BuiltinsSet::Entries(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Entries); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSSetIterator::CreateSetIterator(thread, self, IterationKind::KEY_AND_VALUE); + return iter.GetTaggedValue(); +} + +JSTaggedValue BuiltinsSet::Values(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Values); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSSetIterator::CreateSetIterator(thread, self, IterationKind::VALUE); + return iter.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_set.h b/ecmascript/builtins/builtins_set.h new file mode 100644 index 0000000000000000000000000000000000000000..c7fd3da2b56b6835a7b6ae383fcb05d902c3e581 --- /dev/null +++ b/ecmascript/builtins/builtins_set.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_SET_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_SET_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsSet : public base::BuiltinsBase { +public: + // 23.2.1.1 + static JSTaggedValue SetConstructor(EcmaRuntimeCallInfo *argv); + // 23.2.2.2 + static JSTaggedValue Species(EcmaRuntimeCallInfo *argv); + // 23.2.3.1 + static JSTaggedValue Add(EcmaRuntimeCallInfo *argv); + // 23.2.3.2 + static JSTaggedValue Clear(EcmaRuntimeCallInfo *argv); + // 23.2.3.4 + static JSTaggedValue Delete(EcmaRuntimeCallInfo *argv); + // 23.2.3.5 + static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); + // 23.2.3.6 + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + // 23.2.3.7 + static JSTaggedValue Has(EcmaRuntimeCallInfo *argv); + // 23.2.3.9 + static JSTaggedValue GetSize(EcmaRuntimeCallInfo *argv); + // 23.2.3.10 + static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_SET_H diff --git a/ecmascript/builtins/builtins_string.cpp b/ecmascript/builtins/builtins_string.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b8d4a4527ccacd611c73ee45839d43f2d5280e7 --- /dev/null +++ b/ecmascript/builtins/builtins_string.cpp @@ -0,0 +1,1405 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_string.h" + +#include + +#include "ecmascript/base/number_helper.h" +#include "ecmascript/base/string_helper.h" +#include "ecmascript/builtins/builtins_json.h" +#include "ecmascript/builtins/builtins_regexp.h" +#include "ecmascript/builtins/builtins_symbol.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_string_iterator.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tagged_array.h" +#include "unicode/normalizer2.h" +#include "unicode/normlzr.h" +#include "unicode/unistr.h" + +namespace panda::ecmascript::builtins { +using ObjectFactory = ecmascript::ObjectFactory; +using JSArray = ecmascript::JSArray; + +// 21.1.1.1 String(value) +JSTaggedValue BuiltinsString::StringConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newTarget = GetNewTarget(argv); + if (argv->GetArgsNumber() > 0) { + JSHandle valTagNew = GetCallArg(argv, 0); + if (newTarget->IsUndefined() && valTagNew->IsSymbol()) { + return BuiltinsSymbol::SymbolDescriptiveString(thread, valTagNew.GetTaggedValue()); + } + JSHandle str = JSTaggedValue::ToString(thread, valTagNew); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (newTarget->IsUndefined()) { + return str.GetTaggedValue(); + } + JSHandle strTag(str); + return JSPrimitiveRef::StringCreate(thread, strTag).GetTaggedValue(); + } + JSHandle val = factory->GetEmptyString(); + JSHandle valTag(val); + if (newTarget->IsUndefined()) { + return factory->GetEmptyString().GetTaggedValue(); + } + return JSPrimitiveRef::StringCreate(thread, valTag).GetTaggedValue(); +} + +// 21.1.2.1 +JSTaggedValue BuiltinsString::FromCharCode(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, FromCharCode); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int32_t argLength = argv->GetArgsNumber(); + if (argLength == 0) { + return factory->GetEmptyString().GetTaggedValue(); + } + JSHandle codePointTag = BuiltinsString::GetCallArg(argv, 0); + uint16_t codePointValue = JSTaggedValue::ToUint16(thread, codePointTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle strHandle = factory->NewFromUtf16Literal(&codePointValue, 1); + if (argLength == 1) { + return strHandle.GetTaggedValue(); + } + std::u16string u16str = base::StringHelper::Utf16ToU16String(&codePointValue, 1); + CVector valueTable; + valueTable.reserve(argLength - 1); + for (int32_t i = 1; i < argLength; i++) { + JSHandle nextCp = BuiltinsString::GetCallArg(argv, i); + uint16_t nextCv = JSTaggedValue::ToUint16(thread, nextCp); + valueTable.emplace_back(nextCv); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + std::u16string nextU16str = base::StringHelper::Utf16ToU16String(valueTable.data(), argLength - 1); + u16str = base::StringHelper::Append(u16str, nextU16str); + const char16_t *constChar16tData = u16str.data(); + auto *char16tData = const_cast(constChar16tData); + auto *uint16tData = reinterpret_cast(char16tData); + int32_t u16strSize = u16str.size(); + return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue(); +} + +// 21.1.2.2 +JSTaggedValue BuiltinsString::FromCodePoint(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, FromCodePoint); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int32_t argLength = argv->GetArgsNumber(); + if (argLength == 0) { + return factory->GetEmptyString().GetTaggedValue(); + } + std::u16string u16str; + int32_t u16strSize = argLength; + for (int i = 0; i < argLength; i++) { + JSHandle nextCpTag = BuiltinsString::GetCallArg(argv, i); + JSTaggedNumber nextCpVal = JSTaggedValue::ToNumber(thread, nextCpTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!nextCpVal.IsInteger()) { + THROW_RANGE_ERROR_AND_RETURN(thread, "is not integer", JSTaggedValue::Exception()); + } + int32_t cp = nextCpVal.ToInt32(); + if (cp < 0 || cp > ENCODE_MAX_UTF16) { + THROW_RANGE_ERROR_AND_RETURN(thread, "CodePoint < 0 or CodePoint > 0x10FFFF", JSTaggedValue::Exception()); + } + if (cp == 0) { + CVector data{0x00}; + return factory->NewFromUtf16Literal(data.data(), 1).GetTaggedValue(); + } + if (cp > UINT16_MAX) { + uint16_t cu1 = std::floor((cp - ENCODE_SECOND_FACTOR) / ENCODE_FIRST_FACTOR) + ENCODE_LEAD_LOW; + uint16_t cu2 = ((cp - ENCODE_SECOND_FACTOR) % ENCODE_FIRST_FACTOR) + ENCODE_TRAIL_LOW; + std::u16string nextU16str1 = base::StringHelper::Utf16ToU16String(&cu1, 1); + std::u16string nextU16str2 = base::StringHelper::Utf16ToU16String(&cu2, 1); + u16str = base::StringHelper::Append(u16str, nextU16str1); + u16str = base::StringHelper::Append(u16str, nextU16str2); + u16strSize++; + } else { + auto u16tCp = static_cast(cp); + std::u16string nextU16str = base::StringHelper::Utf16ToU16String(&u16tCp, 1); + u16str = base::StringHelper::Append(u16str, nextU16str); + } + } + const char16_t *constChar16tData = u16str.data(); + auto *char16tData = const_cast(constChar16tData); + auto *uint16tData = reinterpret_cast(char16tData); + return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue(); +} + +// 21.1.2.4 +JSTaggedValue BuiltinsString::Raw(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Raw); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // Let cooked be ToObject(template). + JSHandle cooked = JSTaggedValue::ToObject(thread, BuiltinsString::GetCallArg(argv, 0)); + // ReturnIfAbrupt(cooked). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // Let raw be ToObject(Get(cooked, "raw")). + JSHandle rawKey(factory->NewFromString("raw")); + JSHandle rawTag = + JSObject::GetProperty(thread, JSHandle::Cast(cooked), rawKey).GetValue(); + JSHandle rawObj = JSTaggedValue::ToObject(thread, rawTag); + // ReturnIfAbrupt(rawObj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle rawLen = + JSObject::GetProperty(thread, JSHandle::Cast(rawObj), lengthKey).GetValue(); + // ReturnIfAbrupt(rawLen). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedNumber lengthNumber = JSTaggedValue::ToLength(thread, rawLen); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int length = lengthNumber.ToUint32(); + if (length <= 0) { + return factory->GetEmptyString().GetTaggedValue(); + } + + std::u16string u16str; + int argc = static_cast(argv->GetArgsNumber()) - 1; + for (int i = 0, argsI = 1; i < length; ++i, ++argsI) { + // Let nextSeg be ToString(Get(raw, nextKey)). + JSHandle elementString = + JSObject::GetProperty(thread, JSHandle::Cast(rawObj), i).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + EcmaString *nextSeg = *JSTaggedValue::ToString(thread, elementString); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (nextSeg->IsUtf16()) { + u16str += base::StringHelper::Utf16ToU16String(nextSeg->GetDataUtf16(), nextSeg->GetLength()); + } else { + u16str += base::StringHelper::Utf8ToU16String(nextSeg->GetDataUtf8(), nextSeg->GetLength()); + } + if (i + 1 == length) { + break; + } + if (argsI <= argc) { + EcmaString *nextSub = *JSTaggedValue::ToString(thread, GetCallArg(argv, argsI)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (nextSub->IsUtf16()) { + u16str += base::StringHelper::Utf16ToU16String(nextSub->GetDataUtf16(), nextSub->GetLength()); + } else { + u16str += base::StringHelper::Utf8ToU16String(nextSub->GetDataUtf8(), nextSub->GetLength()); + } + } + } + // return the result string + auto *uint16tData = reinterpret_cast(const_cast(u16str.data())); + return factory->NewFromUtf16Literal(uint16tData, u16str.size()).GetTaggedValue(); +} + +// 21.1.3.1 +JSTaggedValue BuiltinsString::CharAt(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, CharAt); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle posTag = BuiltinsString::GetCallArg(argv, 0); + int32_t pos; + if (posTag->IsUndefined()) { + pos = 0; + } else { + JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + pos = posVal.ToInt32(); + } + if (pos < 0 || pos >= thisLen) { + return factory->GetEmptyString().GetTaggedValue(); + } + uint16_t res = thisHandle->At(pos); + return factory->NewFromUtf16Literal(&res, 1).GetTaggedValue(); +} + +// 21.1.3.2 +JSTaggedValue BuiltinsString::CharCodeAt(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, CharCodeAt); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle posTag = BuiltinsString::GetCallArg(argv, 0); + int32_t pos; + if (posTag->IsUndefined()) { + pos = 0; + } else { + JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + pos = posVal.ToInt32(); + } + if (pos < 0 || pos >= thisLen) { + return GetTaggedDouble(base::NAN_VALUE); + } + uint16_t ret = thisHandle->At(pos); + return GetTaggedInt(ret); +} + +// 21.1.3.3 +JSTaggedValue BuiltinsString::CodePointAt(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, CodePointAt); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle posTag = BuiltinsString::GetCallArg(argv, 0); + + JSTaggedNumber posVal = JSTaggedValue::ToNumber(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t pos = base::NumberHelper::DoubleInRangeInt32(posVal.GetNumber()); + int32_t thisLen = thisHandle->GetLength(); + if (pos < 0 || pos >= thisLen) { + return JSTaggedValue::Undefined(); + } + uint16_t first = thisHandle->At(pos); + if (first < base::utf_helper::DECODE_LEAD_LOW || first > base::utf_helper::DECODE_LEAD_HIGH || pos + 1 == thisLen) { + return GetTaggedInt(first); + } + uint16_t second = thisHandle->At(pos + 1); + if (second < base::utf_helper::DECODE_TRAIL_LOW || second > base::utf_helper::DECODE_TRAIL_HIGH) { + return GetTaggedInt(first); + } + uint32_t res = base::utf_helper::UTF16Decode(first, second); + return GetTaggedInt(res); +} + +// 21.1.3.4 +JSTaggedValue BuiltinsString::Concat(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Concat); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + int32_t argLength = argv->GetArgsNumber(); + if (argLength == 0) { + return thisHandle.GetTaggedValue(); + } + std::u16string u16strThis; + std::u16string u16strNext; + if (thisHandle->IsUtf16()) { + u16strThis = base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + u16strThis = base::StringHelper::Utf8ToU16String(thisHandle->GetDataUtf8(), thisLen); + } + for (int i = 0; i < argLength; i++) { + JSHandle nextTag = BuiltinsString::GetCallArg(argv, i); + JSHandle nextHandle = JSTaggedValue::ToString(thread, nextTag); + int32_t nextLen = nextHandle->GetLength(); + if (nextHandle->IsUtf16()) { + u16strNext = base::StringHelper::Utf16ToU16String(nextHandle->GetDataUtf16(), nextLen); + } else { + u16strNext = base::StringHelper::Utf8ToU16String(nextHandle->GetDataUtf8(), nextLen); + } + u16strThis = base::StringHelper::Append(u16strThis, u16strNext); + } + const char16_t *constChar16tData = u16strThis.data(); + auto *char16tData = const_cast(constChar16tData); + auto *uint16tData = reinterpret_cast(char16tData); + int32_t u16strSize = u16strThis.size(); + return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue(); +} + +// 21.1.3.5 String.prototype.constructor +// 21.1.3.6 +JSTaggedValue BuiltinsString::EndsWith(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, EndsWith); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool isRegexp = JSObject::IsRegExp(thread, searchTag); + if (isRegexp) { + THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception()); + } + JSHandle searchHandle = JSTaggedValue::ToString(thread, searchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + int32_t searchLen = searchHandle->GetLength(); + int32_t pos; + JSHandle posTag = BuiltinsString::GetCallArg(argv, 1); + if (posTag->IsUndefined()) { + pos = thisLen; + } else { + JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + pos = posVal.ToInt32(); + } + int32_t end = std::min(std::max(pos, 0), thisLen); + int32_t start = end - searchLen; + if (start < 0) { + return BuiltinsString::GetTaggedBoolean(false); + } + std::u16string u16strThis; + std::u16string u16strSearch; + if (thisHandle->IsUtf16()) { + u16strThis = base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + if (searchHandle->IsUtf16()) { + u16strSearch = base::StringHelper::Utf16ToU16String(searchHandle->GetDataUtf16(), searchLen); + } else { + const uint8_t *uint8Search = searchHandle->GetDataUtf8(); + u16strSearch = base::StringHelper::Utf8ToU16String(uint8Search, searchLen); + } + int32_t idx = base::StringHelper::Find(u16strThis, u16strSearch, start); + if (idx == start) { + return BuiltinsString::GetTaggedBoolean(true); + } + return BuiltinsString::GetTaggedBoolean(false); +} + +// 21.1.3.7 +JSTaggedValue BuiltinsString::Includes(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Includes); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool isRegexp = JSObject::IsRegExp(thread, searchTag); + if (isRegexp) { + THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception()); + } + JSHandle searchHandle = JSTaggedValue::ToString(thread, searchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + int32_t searchLen = searchHandle->GetLength(); + int32_t pos = 0; + JSHandle posTag = BuiltinsBase::GetCallArg(argv, 1); + if (argv->GetArgsNumber() == 1) { + pos = 0; + } else { + JSTaggedNumber posVal = JSTaggedValue::ToNumber(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + pos = base::NumberHelper::DoubleInRangeInt32(posVal.GetNumber()); + } + int32_t start = std::min(std::max(pos, 0), thisLen); + std::u16string u16strThis; + std::u16string u16strSearch; + if (thisHandle->IsUtf16()) { + u16strThis = base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + if (searchHandle->IsUtf16()) { + u16strSearch = base::StringHelper::Utf16ToU16String(searchHandle->GetDataUtf16(), searchLen); + } else { + const uint8_t *uint8Search = searchHandle->GetDataUtf8(); + u16strSearch = base::StringHelper::Utf8ToU16String(uint8Search, searchLen); + } + int32_t idx = base::StringHelper::Find(u16strThis, u16strSearch, start); + if (idx < 0 || idx > thisLen) { + return BuiltinsString::GetTaggedBoolean(false); + } + return BuiltinsString::GetTaggedBoolean(true); +} + +// 21.1.3.8 +JSTaggedValue BuiltinsString::IndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, IndexOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle searchHandle = JSTaggedValue::ToString(thread, searchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t searchLen = searchHandle->GetLength(); + int32_t pos; + if (argv->GetArgsNumber() == 1) { + pos = 0; + } else { + JSHandle posTag = BuiltinsString::GetCallArg(argv, 1); + JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + pos = posVal.ToInt32(); + } + pos = std::min(std::max(pos, 0), thisLen); + std::u16string u16strThis; + std::u16string u16strSearch; + if (thisHandle->IsUtf16()) { + u16strThis = base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + if (searchHandle->IsUtf16()) { + u16strSearch = base::StringHelper::Utf16ToU16String(searchHandle->GetDataUtf16(), searchLen); + } else { + const uint8_t *uint8Search = searchHandle->GetDataUtf8(); + u16strSearch = base::StringHelper::Utf8ToU16String(uint8Search, searchLen); + } + int32_t res = base::StringHelper::Find(u16strThis, u16strSearch, pos); + if (res >= 0 && res < thisLen) { + return GetTaggedInt(res); + } + res = -1; + return GetTaggedInt(res); +} + +// 21.1.3.9 +JSTaggedValue BuiltinsString::LastIndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, LastIndexOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle searchHandle = JSTaggedValue::ToString(thread, searchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t searchLen = searchHandle->GetLength(); + int32_t pos; + if (argv->GetArgsNumber() == 1) { + pos = thisLen; + } else { + JSHandle posTag = BuiltinsString::GetCallArg(argv, 1); + JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (std::isnan(JSTaggedValue::ToNumber(thread, posTag).GetNumber())) { + pos = thisLen; + } else { + pos = posVal.ToInt32(); + } + } + pos = std::min(std::max(pos, 0), thisLen); + std::u16string u16strThis; + std::u16string u16strSearch; + if (thisHandle->IsUtf16()) { + u16strThis = base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + if (searchHandle->IsUtf16()) { + u16strSearch = base::StringHelper::Utf16ToU16String(searchHandle->GetDataUtf16(), searchLen); + } else { + const uint8_t *uint8Search = searchHandle->GetDataUtf8(); + u16strSearch = base::StringHelper::Utf8ToU16String(uint8Search, searchLen); + } + int32_t res = base::StringHelper::RFind(u16strThis, u16strSearch, pos); + if (res >= 0 && res < thisLen) { + return GetTaggedInt(res); + } + res = -1; + return GetTaggedInt(res); +} + +// 21.1.3.10 +JSTaggedValue BuiltinsString::LocaleCompare(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, LocaleCompare); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle that_tag = BuiltinsString::GetCallArg(argv, 0); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thatHandle = JSTaggedValue::ToString(thread, that_tag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t res = thisHandle->Compare(*thatHandle); + return GetTaggedInt(res); +} + +// 21.1.3.11 +JSTaggedValue BuiltinsString::Match(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Match); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle regexp = BuiltinsString::GetCallArg(argv, 0); + JSHandle matchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchSymbol(); + if (!regexp->IsUndefined() && !regexp->IsNull()) { + if (regexp->IsECMAObject()) { + JSHandle matcher = JSObject::GetMethod(thread, regexp, matchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!matcher->IsUndefined()) { + ASSERT(matcher->IsJSFunction()); + JSHandle arg = factory->NewTaggedArray(1); + arg->Set(thread, 0, thisTag); + return JSFunction::Call(thread, matcher, regexp, arg); + } + } + } + JSHandle thisVal = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle undifinedHandle = globalConst->GetHandledUndefined(); + JSHandle rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undifinedHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle arguments = factory->NewTaggedArray(1); + arguments->Set(thread, 0, thisVal.GetTaggedValue()); + return JSFunction::Invoke(thread, rx, matchTag, arguments); +} + +// 21.1.3.13 +JSTaggedValue BuiltinsString::Repeat(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Repeat); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle countTag = BuiltinsString::GetCallArg(argv, 0); + JSTaggedNumber num = JSTaggedValue::ToInteger(thread, countTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double d = num.GetNumber(); + if (d < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "less than 0", JSTaggedValue::Exception()); + } + if (d == base::POSITIVE_INFINITY) { + THROW_RANGE_ERROR_AND_RETURN(thread, "is infinity", JSTaggedValue::Exception()); + } + int32_t count = base::NumberHelper::DoubleInRangeInt32(d); + std::u16string u16strThis; + if (thisHandle->IsUtf16()) { + u16strThis = base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + if (thisLen == 0) { + return thisHandle.GetTaggedValue(); + } + + EcmaString *res = base::StringHelper::Repeat(thread, u16strThis, count); + return JSTaggedValue(res); +} + +// 21.1.3.14 +JSTaggedValue BuiltinsString::Replace(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Replace); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); + JSHandle replaceTag = BuiltinsString::GetCallArg(argv, 1); + + ObjectFactory *factory = ecmaVm->GetFactory(); + + // If searchValue is neither undefined nor null, then + if (searchTag->IsECMAObject()) { + JSHandle replaceKey = env->GetReplaceSymbol(); + // Let replacer be GetMethod(searchValue, @@replace). + JSHandle replaceMethod = JSObject::GetMethod(thread, searchTag, replaceKey); + // ReturnIfAbrupt(replacer). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // If replacer is not undefined, then + if (!replaceMethod->IsUndefined()) { + // Return Call(replacer, searchValue, «O, replaceValue»). + JSHandle args = factory->NewTaggedArray(2); // 2: 2 means two args stored in array + args->Set(thread, 0, thisTag); + args->Set(thread, 1, replaceTag); + return JSFunction::Call(thread, replaceMethod, searchTag, args); + } + } + + // Let string be ToString(O). + JSHandle thisString = JSTaggedValue::ToString(thread, thisTag); + // ReturnIfAbrupt(string). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // Let searchString be ToString(searchValue). + JSHandle searchString = JSTaggedValue::ToString(thread, searchTag); + // ReturnIfAbrupt(searchString). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // Let functionalReplace be IsCallable(replaceValue). + if (!replaceTag->IsCallable()) { + // If functionalReplace is false, then + // Let replaceValue be ToString(replaceValue). + // ReturnIfAbrupt(replaceValue) + replaceTag = JSHandle(JSTaggedValue::ToString(thread, replaceTag)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // Search string for the first occurrence of searchString and let pos be the index within string of the first code + // unit of the matched substring and let matched be searchString. If no occurrences of searchString were found, + // return string. + int32_t pos = thisString->IndexOf(*searchString); + if (pos == -1) { + return thisString.GetTaggedValue(); + } + + JSMutableHandle replHandle(thread, factory->GetEmptyString().GetTaggedValue()); + // If functionalReplace is true, then + if (replaceTag->IsCallable()) { + // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»). + JSHandle jsargs = factory->NewTaggedArray(3); // 3: «matched, pos, and string» + jsargs->Set(thread, 0, searchString); + jsargs->Set(thread, 1, JSTaggedValue(pos)); + jsargs->Set(thread, 2, thisString); // 2: Position of string + JSHandle replaceFunc(replaceTag); + JSTaggedValue replStrDeocodeValue = + JSFunction::Call(thread, replaceTag, globalConst->GetHandledUndefined(), jsargs); + replHandle.Update(replStrDeocodeValue); + } else { + // Let captures be an empty List. + JSHandle capturesList = factory->NewTaggedArray(0); + ASSERT_PRINT(replaceTag->IsString(), "replace must be string"); + JSHandle replacement(thread, replaceTag->GetTaggedObject()); + // Let replStr be GetSubstitution(matched, string, pos, captures, replaceValue) + replHandle.Update(GetSubstitution(thread, searchString, thisString, pos, capturesList, replacement)); + } + JSHandle realReplaceStr = JSTaggedValue::ToString(thread, replHandle); + // Let tailPos be pos + the number of code units in matched. + int32_t tailPos = pos + searchString->GetLength(); + // Let newString be the String formed by concatenating the first pos code units of string, replStr, and the trailing + // substring of string starting at index tailPos. If pos is 0, the first element of the concatenation will be the + // empty String. + // Return newString. + JSHandle prefixString(thread, EcmaString::FastSubString(thisString, 0, pos, ecmaVm)); + JSHandle suffixString( + thread, EcmaString::FastSubString(thisString, tailPos, thisString->GetLength() - tailPos, ecmaVm)); + std::u16string stringBuilder; + if (prefixString->IsUtf16()) { + const uint16_t *data = prefixString->GetDataUtf16(); + stringBuilder += base::StringHelper::Utf16ToU16String(data, prefixString->GetLength()); + } else { + const uint8_t *data = prefixString->GetDataUtf8(); + stringBuilder += base::StringHelper::Utf8ToU16String(data, prefixString->GetLength()); + } + + if (realReplaceStr->IsUtf16()) { + const uint16_t *data = realReplaceStr->GetDataUtf16(); + stringBuilder += base::StringHelper::Utf16ToU16String(data, realReplaceStr->GetLength()); + } else { + const uint8_t *data = realReplaceStr->GetDataUtf8(); + stringBuilder += base::StringHelper::Utf8ToU16String(data, realReplaceStr->GetLength()); + } + + if (suffixString->IsUtf16()) { + const uint16_t *data = suffixString->GetDataUtf16(); + stringBuilder += base::StringHelper::Utf16ToU16String(data, suffixString->GetLength()); + } else { + const uint8_t *data = suffixString->GetDataUtf8(); + stringBuilder += base::StringHelper::Utf8ToU16String(data, suffixString->GetLength()); + } + + auto *char16tData = const_cast(stringBuilder.c_str()); + auto *uint16tData = reinterpret_cast(char16tData); + return factory->NewFromUtf16Literal(uint16tData, stringBuilder.size()).GetTaggedValue(); +} + +JSTaggedValue BuiltinsString::GetSubstitution(JSThread *thread, const JSHandle &matched, + const JSHandle &srcString, int position, + const JSHandle &captureList, + const JSHandle &replacement) +{ + BUILTINS_API_TRACE(thread, String, GetSubstitution); + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle dollarString = factory->NewFromString("$"); + int32_t replaceLength = replacement->GetLength(); + int32_t tailPos = position + matched->GetLength(); + + int32_t nextDollarIndex = replacement->IndexOf(*dollarString, 0); + if (nextDollarIndex < 0) { + return replacement.GetTaggedValue(); + } + + std::u16string stringBuilder; + if (nextDollarIndex > 0) { + if (replacement->IsUtf16()) { + const uint16_t *data = replacement->GetDataUtf16(); + stringBuilder += base::StringHelper::Utf16ToU16String(data, nextDollarIndex); + } else { + const uint8_t *data = replacement->GetDataUtf8(); + stringBuilder += base::StringHelper::Utf8ToU16String(data, nextDollarIndex); + } + } + + while (true) { + int peekIndex = nextDollarIndex + 1; + if (peekIndex >= replaceLength) { + stringBuilder += '$'; + auto *char16tData = const_cast(stringBuilder.c_str()); + auto *uint16tData = reinterpret_cast(char16tData); + return factory->NewFromUtf16Literal(uint16tData, stringBuilder.length()).GetTaggedValue(); + } + int continueFromIndex = -1; + uint16_t peek = replacement->At(peekIndex); + switch (peek) { + case '$': // $$ + stringBuilder += '$'; + continueFromIndex = peekIndex + 1; + break; + case '&': // $& - match + if (matched->IsUtf16()) { + const uint16_t *data = matched->GetDataUtf16(); + stringBuilder += base::StringHelper::Utf16ToU16String(data, matched->GetLength()); + } else { + const uint8_t *data = matched->GetDataUtf8(); + stringBuilder += base::StringHelper::Utf8ToU16String(data, matched->GetLength()); + } + continueFromIndex = peekIndex + 1; + break; + case '`': // $` - prefix + if (position > 0) { + EcmaString *prefix = EcmaString::FastSubString(srcString, 0, position, ecmaVm); + if (prefix->IsUtf16()) { + const uint16_t *data = prefix->GetDataUtf16(); + stringBuilder += base::StringHelper::Utf16ToU16String(data, prefix->GetLength()); + } else { + const uint8_t *data = prefix->GetDataUtf8(); + stringBuilder += base::StringHelper::Utf8ToU16String(data, prefix->GetLength()); + } + } + continueFromIndex = peekIndex + 1; + break; + case '\'': { + // $' - suffix + int32_t srcLength = srcString->GetLength(); + if (tailPos < srcLength) { + EcmaString *sufffix = EcmaString::FastSubString(srcString, tailPos, srcLength - tailPos, ecmaVm); + if (sufffix->IsUtf16()) { + const uint16_t *data = sufffix->GetDataUtf16(); + stringBuilder += base::StringHelper::Utf16ToU16String(data, sufffix->GetLength()); + } else { + const uint8_t *data = sufffix->GetDataUtf8(); + stringBuilder += base::StringHelper::Utf8ToU16String(data, sufffix->GetLength()); + } + } + continueFromIndex = peekIndex + 1; + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + int capturesLength = captureList->GetLength(); + // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99 + int32_t scaledIndex = (peek - '0'); + int32_t advance = 1; + if (peekIndex + 1 < replaceLength) { + uint16_t nextPeek = replacement->At(peekIndex + 1); + if (nextPeek >= '0' && nextPeek <= '9') { + constexpr int32_t TEN_BASE = 10; + int32_t newScaledIndex = scaledIndex * TEN_BASE + (nextPeek - '0'); + if (newScaledIndex <= capturesLength) { + scaledIndex = newScaledIndex; + advance = 2; // 2: 2 means from index needs to add two. + } + } + } + + if (scaledIndex == 0 || scaledIndex > capturesLength) { + stringBuilder += '$'; + continueFromIndex = peekIndex; + break; + } + + JSTaggedValue capturesVal(captureList->Get(scaledIndex - 1)); + if (!capturesVal.IsUndefined()) { + EcmaString *captureString = EcmaString::Cast(capturesVal.GetTaggedObject()); + if (captureString->IsUtf16()) { + const uint16_t *data = captureString->GetDataUtf16(); + stringBuilder += base::StringHelper::Utf16ToU16String(data, captureString->GetLength()); + } else { + const uint8_t *data = captureString->GetDataUtf8(); + stringBuilder += base::StringHelper::Utf8ToU16String(data, captureString->GetLength()); + } + } + continueFromIndex = peekIndex + advance; + break; + } + default: + stringBuilder += '$'; + continueFromIndex = peekIndex; + break; + } + // Go the the next $ in the replacement. + nextDollarIndex = replacement->IndexOf(*dollarString, continueFromIndex); + if (nextDollarIndex < 0) { + if (continueFromIndex < replaceLength) { + EcmaString *nextAppend = EcmaString::FastSubString(replacement, continueFromIndex, + replaceLength - continueFromIndex, ecmaVm); + if (nextAppend->IsUtf16()) { + const uint16_t *data = nextAppend->GetDataUtf16(); + stringBuilder += base::StringHelper::Utf16ToU16String(data, nextAppend->GetLength()); + } else { + const uint8_t *data = nextAppend->GetDataUtf8(); + stringBuilder += base::StringHelper::Utf8ToU16String(data, nextAppend->GetLength()); + } + } + auto *char16tData = const_cast(stringBuilder.c_str()); + auto *uint16tData = reinterpret_cast(char16tData); + return factory->NewFromUtf16Literal(uint16tData, stringBuilder.length()).GetTaggedValue(); + } + // Append substring between the previous and the next $ character. + if (nextDollarIndex > continueFromIndex) { + EcmaString *nextAppend = + EcmaString::FastSubString(replacement, continueFromIndex, nextDollarIndex - continueFromIndex, ecmaVm); + if (nextAppend->IsUtf16()) { + const uint16_t *data = nextAppend->GetDataUtf16(); + stringBuilder += base::StringHelper::Utf16ToU16String(data, nextAppend->GetLength()); + } else { + const uint8_t *data = nextAppend->GetDataUtf8(); + stringBuilder += base::StringHelper::Utf8ToU16String(data, nextAppend->GetLength()); + } + } + } + UNREACHABLE(); +} + +// 21.1.3.15 +JSTaggedValue BuiltinsString::Search(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Search); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle regexp = BuiltinsString::GetCallArg(argv, 0); + JSHandle searchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetSearchSymbol(); + if (!regexp->IsUndefined() && !regexp->IsNull()) { + if (regexp->IsECMAObject()) { + JSHandle searcher = JSObject::GetMethod(thread, regexp, searchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!searcher->IsUndefined()) { + ASSERT(searcher->IsJSFunction()); + JSHandle arg = factory->NewTaggedArray(1); + arg->Set(thread, 0, thisTag); + return JSFunction::Call(thread, searcher, regexp, arg); + } + } + } + JSHandle thisVal = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle undifinedHandle = globalConst->GetHandledUndefined(); + JSHandle rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undifinedHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle arguments = factory->NewTaggedArray(1); + arguments->Set(thread, 0, thisVal.GetTaggedValue()); + return JSFunction::Invoke(thread, rx, searchTag, arguments); +} + +// 21.1.3.16 +JSTaggedValue BuiltinsString::Slice(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Slice); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle startTag = BuiltinsString::GetCallArg(argv, 0); + JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t start = ConvertDoubleToInt(startVal.GetNumber()); + int32_t end; + JSHandle endTag = BuiltinsString::GetCallArg(argv, 1); + if (endTag->IsUndefined()) { + end = thisLen; + } else { + JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + end = ConvertDoubleToInt(endVal.GetNumber()); + } + int32_t from; + int32_t to; + if (start < 0) { + from = std::max(start + thisLen, 0); + } else { + from = std::min(start, thisLen); + } + if (end < 0) { + to = std::max(end + thisLen, 0); + } else { + to = std::min(end, thisLen); + } + int32_t len = std::max(to - from, 0); + return JSTaggedValue(EcmaString::FastSubString(thisHandle, from, len, thread->GetEcmaVM())); +} + +// 21.1.3.17 +JSTaggedValue BuiltinsString::Split(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Split); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + + // Let O be RequireObjectCoercible(this value). + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisObj(thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle seperatorTag = BuiltinsString::GetCallArg(argv, 0); + JSHandle limitTag = BuiltinsString::GetCallArg(argv, 1); + ObjectFactory *factory = ecmaVm->GetFactory(); + // If separator is neither undefined nor null, then + if (seperatorTag->IsECMAObject()) { + JSHandle splitKey = env->GetSplitSymbol(); + // Let splitter be GetMethod(separator, @@split). + JSHandle splitter = JSObject::GetMethod(thread, seperatorTag, splitKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!splitter->IsUndefined()) { + // Return Call(splitter, separator, «‍O, limit»). + JSHandle jsargs = factory->NewTaggedArray(2); // 2: 2 means two args stored in array + jsargs->Set(thread, 0, thisTag); + jsargs->Set(thread, 1, limitTag); + return JSFunction::Call(thread, splitter, seperatorTag, jsargs); + } + } + // Let S be ToString(O). + JSHandle thisString = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // Let A be ArrayCreate(0). + JSHandle resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + uint32_t arrayLength = 0; + // If limit is undefined, let lim = 2^53–1; else let lim = ToLength(limit). + uint32_t lim; + if (limitTag->IsUndefined()) { + lim = UINT32_MAX - 1; + } else { + lim = JSTaggedValue::ToInteger(thread, limitTag).ToUint32(); + } + // ReturnIfAbrupt(lim). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // If lim = 0, return A. + if (lim == 0) { + return resultArray.GetTaggedValue(); + } + // Let s be the number of elements in S. + int32_t thisLength = thisString->GetLength(); + JSHandle seperatorString = JSTaggedValue::ToString(thread, seperatorTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (seperatorTag->IsUndefined()) { + // Perform CreateDataProperty(A, "0", S). + JSHandle zeroKey(thread, JSTaggedValue(0)); + JSObject::CreateDataProperty(thread, resultArray, zeroKey, JSHandle(thisString)); + ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception"); + return resultArray.GetTaggedValue(); + } + // If S.length = 0, then + if (thisLength == 0) { + if (SplitMatch(thisString, 0, seperatorString) != -1) { + return resultArray.GetTaggedValue(); + } + JSHandle zeroKey(thread, JSTaggedValue(0)); + JSObject::CreateDataProperty(thread, resultArray, zeroKey, JSHandle(thisString)); + ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception"); + return resultArray.GetTaggedValue(); + } + + // Let q = p. + // Repeat, while q ≠ s + int32_t p = 0; + int32_t q = p; + while (q != thisLength) { + int32_t matchedIndex = SplitMatch(thisString, q, seperatorString); + if (matchedIndex == -1) { + q = q + 1; + } else { + if (matchedIndex == p) { + q = q + 1; + } else { + EcmaString *elementString = EcmaString::FastSubString(thisString, p, q - p, ecmaVm); + JSHandle arrayLengthKey(thread, JSTaggedValue(arrayLength)); + JSHandle elementTag(thread, elementString); + JSObject::CreateDataProperty(thread, resultArray, arrayLengthKey, elementTag); + ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception"); + arrayLength = arrayLength + 1; + if (arrayLength == lim) { + return resultArray.GetTaggedValue(); + } + p = matchedIndex; + q = p; + } + } + } + EcmaString *elementString = EcmaString::FastSubString(thisString, p, thisLength - p, ecmaVm); + JSHandle arrayLengthKey(thread, JSTaggedValue(arrayLength)); + JSHandle elementTag(thread, elementString); + JSObject::CreateDataProperty(thread, resultArray, arrayLengthKey, elementTag); + ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception"); + return resultArray.GetTaggedValue(); +} + +int32_t BuiltinsString::SplitMatch(const JSHandle &str, int32_t q, const JSHandle ®) +{ + int32_t s = str->GetLength(); + int32_t r = reg->GetLength(); + if (q + r > s) { + return -1; + } + int32_t i = 0; + for (i = 0; i < r; i++) { + if (str->At(q + i) != reg->At(i)) { + return -1; + } + } + return q + r; +} + +// 21.1.3.18 +JSTaggedValue BuiltinsString::StartsWith(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, StartsWith); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); + + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool isRegexp = JSObject::IsRegExp(thread, searchTag); + if (isRegexp) { + THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception()); + } + + JSHandle searchHandle = JSTaggedValue::ToString(thread, searchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + int32_t searchLen = searchHandle->GetLength(); + int32_t pos; + JSHandle posTag = BuiltinsString::GetCallArg(argv, 1); + if (posTag->IsUndefined()) { + pos = 0; + } else { + JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + pos = posVal.ToInt32(); + } + pos = std::min(std::max(pos, 0), thisLen); + if (pos + searchLen > thisLen) { + return BuiltinsString::GetTaggedBoolean(false); + } + std::u16string u16strThis; + std::u16string u16strSearch; + if (thisHandle->IsUtf16()) { + u16strThis = base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + if (searchHandle->IsUtf16()) { + u16strSearch = base::StringHelper::Utf16ToU16String(searchHandle->GetDataUtf16(), searchLen); + } else { + const uint8_t *uint8Search = searchHandle->GetDataUtf8(); + u16strSearch = base::StringHelper::Utf8ToU16String(uint8Search, searchLen); + } + int32_t idx = base::StringHelper::Find(u16strThis, u16strSearch, pos); + if (idx == pos) { + return BuiltinsString::GetTaggedBoolean(true); + } + return BuiltinsString::GetTaggedBoolean(false); +} + +// 21.1.3.19 +JSTaggedValue BuiltinsString::Substring(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Substring); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle startTag = BuiltinsString::GetCallArg(argv, 0); + JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t start = ConvertDoubleToInt(startVal.GetNumber()); + int32_t end; + JSHandle endTag = BuiltinsString::GetCallArg(argv, 1); + if (endTag->IsUndefined()) { + end = thisLen; + } else { + JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + end = ConvertDoubleToInt(endVal.GetNumber()); + } + start = std::min(std::max(start, 0), thisLen); + end = std::min(std::max(end, 0), thisLen); + int32_t from = std::min(start, end); + int32_t to = std::max(start, end); + int32_t len = to - from; + return JSTaggedValue(EcmaString::FastSubString(thisHandle, from, len, thread->GetEcmaVM())); +} + +// 21.1.3.22 +JSTaggedValue BuiltinsString::ToLowerCase(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, ToLowerCase); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + std::u16string u16strThis; + if (thisHandle->IsUtf16()) { + u16strThis = base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + return JSTaggedValue(base::StringHelper::ToLower(thread, u16strThis)); +} + +// 21.1.3.23 +JSTaggedValue BuiltinsString::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue()); +} + +// 21.1.3.24 +JSTaggedValue BuiltinsString::ToUpperCase(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, ToUpperCase); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + std::u16string u16strThis; + if (thisHandle->IsUtf16()) { + u16strThis = base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + return JSTaggedValue(base::StringHelper::ToUpper(thread, u16strThis)); +} + +// 21.1.3.25 +JSTaggedValue BuiltinsString::Trim(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Trim); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + std::u16string u16strThis; + if (thisHandle->IsUtf16()) { + u16strThis = base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + + EcmaString *str = base::StringHelper::Trim(thread, u16strThis); + return JSTaggedValue(str); +} + +// 21.1.3.26 +JSTaggedValue BuiltinsString::ValueOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue()); +} + +// 21.1.3.27 +JSTaggedValue BuiltinsString::GetStringIterator(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, GetStringIterator); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be RequireObjectCoercible(this value). + JSHandle current(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + // Let S be ToString(O). + + JSHandle string = JSTaggedValue::ToString(thread, current); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + // Return CreateStringIterator(S). + return JSStringIterator::CreateStringIterator(thread, string).GetTaggedValue(); +} + +// B.2.3.1 +JSTaggedValue BuiltinsString::SubStr(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, SubStr); + JSThread *thread = argv->GetThread(); + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be RequireObjectCoercible(this value). + // 2. Let S be ToString(O). + + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisString = JSTaggedValue::ToString(thread, thisTag); + + // 3. ReturnIfAbrupt(S). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle intStart = GetCallArg(argv, 0); + // 4. Let intStart be ToInteger(start). + JSTaggedNumber numStart = JSTaggedValue::ToInteger(thread, intStart); + // 5. ReturnIfAbrupt(intStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t start = numStart.ToInt32(); + JSHandle lengthTag = GetCallArg(argv, 1); + // 6. If length is undefined, let end be +; otherwise let end be ToInteger(length). + int32_t end; + if (lengthTag->IsUndefined()) { + end = INT_MAX; + } else { + JSTaggedNumber lengthNumber = JSTaggedValue::ToInteger(thread, lengthTag); + // 7. ReturnIfAbrupt(end). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + end = lengthNumber.ToInt32(); + } + // 8. Let size be the number of code units in S. + int32_t size = thisString->GetLength(); + // 9. If intStart < 0, let intStart be max(size + intStart,0). + if (start < 0) { + start = std::max(size + start, 0); + } + // 10. Let resultLength be min(max(end,0), size – intStart). + int32_t resultLength = std::min(std::max(end, 0), size - start); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 11. If resultLength  0, return the empty String "". + if (resultLength <= 0) { + return factory->GetEmptyString().GetTaggedValue(); + } + return JSTaggedValue(EcmaString::FastSubString(thisString, start, resultLength, thread->GetEcmaVM())); +} + +JSTaggedValue BuiltinsString::GetLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisHandle = GetThis(argv); + + JSHandle thisString = JSTaggedValue::ToString(thread, thisHandle); + return GetTaggedInt(thisString->GetLength()); +} + +// 21.1.3 +JSTaggedValue BuiltinsString::ThisStringValue(JSThread *thread, JSTaggedValue value) +{ + if (value.IsString()) { + return value; + } + if (value.IsECMAObject()) { + auto jshClass = value.GetTaggedObject()->GetClass(); + if (jshClass->GetObjectType() == JSType::JS_PRIMITIVE_REF) { + JSTaggedValue primitive = JSPrimitiveRef::Cast(value.GetTaggedObject())->GetValue(); + if (primitive.IsString()) { + return primitive; + } + } + } + THROW_TYPE_ERROR_AND_RETURN(thread, "can not convert to String", JSTaggedValue::Exception()); +} + +int32_t BuiltinsString::ConvertDoubleToInt(double d) +{ + if (std::isnan(d) || d == -base::POSITIVE_INFINITY) { + return 0; + } + if (d >= static_cast(INT_MAX)) { + return INT_MAX; + } + if (d <= static_cast(INT_MIN)) { + return INT_MIN; + } + return base::NumberHelper::DoubleToInt(d, base::INT32_BITS); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_string.h b/ecmascript/builtins/builtins_string.h new file mode 100644 index 0000000000000000000000000000000000000000..2371fb720fbe3e043f6bc28421c03ac14b5ee25c --- /dev/null +++ b/ecmascript/builtins/builtins_string.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_STRING_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_STRING_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript::builtins { +constexpr int32_t ENCODE_MAX_UTF16 = 0X10FFFF; +constexpr uint16_t ENCODE_LEAD_LOW = 0xD800; +constexpr uint16_t ENCODE_TRAIL_LOW = 0xDC00; +constexpr uint32_t ENCODE_FIRST_FACTOR = 0x400; +constexpr uint32_t ENCODE_SECOND_FACTOR = 0x10000; +constexpr double DOUBLE_INT_MAX = static_cast(INT_MAX); +constexpr double DOUBLE_INT_MIN = static_cast(INT_MIN); + +class BuiltinsString : public base::BuiltinsBase { +public: + // 21.1.1.1 + static JSTaggedValue StringConstructor(EcmaRuntimeCallInfo *argv); + // 21.1.2.1 + static JSTaggedValue FromCharCode(EcmaRuntimeCallInfo *argv); + // 21.1.2.2 + static JSTaggedValue FromCodePoint(EcmaRuntimeCallInfo *argv); + // 21.1.2.4 + static JSTaggedValue Raw(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue GetSubstitution(JSThread *thread, const JSHandle &matched, + const JSHandle &srcString, int position, + const JSHandle &captureList, + const JSHandle &replacement); + // 21.1.3.1 + static JSTaggedValue CharAt(EcmaRuntimeCallInfo *argv); + // 21.1.3.2 + static JSTaggedValue CharCodeAt(EcmaRuntimeCallInfo *argv); + // 21.1.3.3 + static JSTaggedValue CodePointAt(EcmaRuntimeCallInfo *argv); + // 21.1.3.4 + static JSTaggedValue Concat(EcmaRuntimeCallInfo *argv); + // 21.1.3.5 String.prototype.constructor + // 21.1.3.6 + static JSTaggedValue EndsWith(EcmaRuntimeCallInfo *argv); + // 21.1.3.7 + static JSTaggedValue Includes(EcmaRuntimeCallInfo *argv); + // 21.1.3.8 + static JSTaggedValue IndexOf(EcmaRuntimeCallInfo *argv); + // 21.1.3.9 + static JSTaggedValue LastIndexOf(EcmaRuntimeCallInfo *argv); + // 21.1.3.10 + static JSTaggedValue LocaleCompare(EcmaRuntimeCallInfo *argv); + // 21.1.3.11 + static JSTaggedValue Match(EcmaRuntimeCallInfo *argv); + // 21.1.3.12 + static JSTaggedValue Normalize(EcmaRuntimeCallInfo *argv); + // 21.1.3.13 + static JSTaggedValue Repeat(EcmaRuntimeCallInfo *argv); + // 21.1.3.14 + static JSTaggedValue Replace(EcmaRuntimeCallInfo *argv); + // 21.1.3.14.1 Runtime Semantics: GetSubstitution() + // 21.1.3.15 + static JSTaggedValue Search(EcmaRuntimeCallInfo *argv); + // 21.1.3.16 + static JSTaggedValue Slice(EcmaRuntimeCallInfo *argv); + // 21.1.3.17 + static JSTaggedValue Split(EcmaRuntimeCallInfo *argv); + // 21.1.3.17.1 Runtime Semantics: SplitMatch + // 21.1.3.18 + static JSTaggedValue StartsWith(EcmaRuntimeCallInfo *argv); + // 21.1.3.19 + static JSTaggedValue Substring(EcmaRuntimeCallInfo *argv); + // 21.1.3.20 + static JSTaggedValue ToLocaleLowerCase(EcmaRuntimeCallInfo *argv); + // 21.1.3.21 + static JSTaggedValue ToLocaleUpperCase(EcmaRuntimeCallInfo *argv); + // 21.1.3.22 + static JSTaggedValue ToLowerCase(EcmaRuntimeCallInfo *argv); + // 21.1.3.23 + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 21.1.3.24 + static JSTaggedValue ToUpperCase(EcmaRuntimeCallInfo *argv); + // 21.1.3.25 + static JSTaggedValue Trim(EcmaRuntimeCallInfo *argv); + // 21.1.3.26 + static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv); + // 21.1.3.27 + static JSTaggedValue GetStringIterator(EcmaRuntimeCallInfo *argv); + // 21.1.3 + static JSTaggedValue ThisStringValue(JSThread *thread, JSTaggedValue value); + // 21.1.2.27 + static JSTaggedValue CreateIterator(EcmaRuntimeCallInfo *argv); + // 10.1.2 + static uint16_t UTF16Decode(uint16_t lead, uint16_t trail); + // annexB B.2.3.1 + static JSTaggedValue SubStr(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue GetLength(EcmaRuntimeCallInfo *argv); + +private: + static int32_t ConvertDoubleToInt(double d); + // 21.1.3.17.1 + static int32_t SplitMatch(const JSHandle &str, int32_t q, const JSHandle ®); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_STRING_H diff --git a/ecmascript/builtins/builtins_string_iterator.cpp b/ecmascript/builtins/builtins_string_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..abfa2b643cce57bafe603299a6a23fbac913f4db --- /dev/null +++ b/ecmascript/builtins/builtins_string_iterator.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "builtins_string_iterator.h" +#include "ecmascript/base/string_helper.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/js_string_iterator.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsStringIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), StringIterator, Next); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisValue = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + // 3. If O does not have all of the internal slots of an String Iterator Instance (21.1.5.3), + // throw a TypeError exception. + if (!thisValue->IsStringIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "is not StringIterator", JSTaggedValue::Exception()); + } + // 4. Let s be the value of the [[IteratedString]] internal slot of O. + JSHandle string(thread, thisValue.GetObject()->GetIteratedString()); + // 5. If s is undefined, return CreateIterResultObject(undefined, true). + if (string->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, string, true).GetTaggedValue(); + } + // 6. Let position be the value of the [[StringIteratorNextIndex]] internal slot of O. + double position = JSTaggedNumber(thisValue.GetObject()->GetStringIteratorNextIndex()).GetNumber(); + + // 7. Let len be the number of elements in s. + uint32_t len = string.GetObject()->GetLength(); + // If position ≥ len, then + // a. Set the value of the [[IteratedString]] internal slot of O to + // b. Return CreateIterResultObject(undefined, true). + if (position >= len) { + thisValue.GetObject()->SetIteratedString(thread, JSTaggedValue::Undefined()); + JSHandle result(thread, JSTaggedValue::Undefined()); + return JSIterator::CreateIterResultObject(thread, result, true).GetTaggedValue(); + } + + // 9. Let first be the code unit value at index position in s. + uint16_t first = string.GetObject()->At(position); + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + uint32_t resultSize = 1; + // 10. If first < 0xD800 or first > 0xDBFF or position+1 = len, let resultString be the string consisting of the + // single code unit first. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (position + 1 == len || first < base::utf_helper::DECODE_LEAD_LOW || + first > base::utf_helper::DECODE_LEAD_HIGH) { + std::vector resultString{first, 0x0}; + result.Update(factory->NewFromUtf16(resultString.data(), 1).GetTaggedValue()); + } else { + // 11. Else, + // a. Let second be the code unit value at index position+1 in the String S. + // b. If second < 0xDC00 or second > 0xDFFF, let resultString be the string consisting of the single code unit + // first. + // c. Else, let resultString be the string consisting of the code unit first followed by the code unit second. + uint16_t second = string.GetObject()->At(position + 1); + if (second < base::utf_helper::DECODE_TRAIL_LOW || second > base::utf_helper::DECODE_TRAIL_HIGH) { + std::vector resultString{first, 0x0}; + result.Update(factory->NewFromUtf16(resultString.data(), 1).GetTaggedValue()); + } else { + std::vector resultString{first, second, 0x0}; + result.Update(factory->NewFromUtf16(resultString.data(), 2).GetTaggedValue()); // 2: two bytes + resultSize = 2; // 2: 2 means that two bytes represent a character string + } + } + // 12. Let resultSize be the number of code units in resultString. + // 13. Set the value of the [[StringIteratorNextIndex]] internal slot of O to position+ resultSize. + thisValue.GetObject()->SetStringIteratorNextIndex(thread, JSTaggedValue(position + resultSize)); + + // 14. Return CreateIterResultObject(resultString, false). + return JSIterator::CreateIterResultObject(thread, result, false).GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_string_iterator.h b/ecmascript/builtins/builtins_string_iterator.h new file mode 100644 index 0000000000000000000000000000000000000000..98a6c86d596a49132b1b9b6a5707500b004f9a48 --- /dev/null +++ b/ecmascript/builtins/builtins_string_iterator.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_STRING_ITERATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_STRING_ITERATOR_H + +#include "ecmascript/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsStringIterator : public base::BuiltinsBase { +public: + // 21.1.5.2.1 + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_STRING_ITERATOR_H diff --git a/ecmascript/builtins/builtins_symbol.cpp b/ecmascript/builtins/builtins_symbol.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba54f1536f9ca137fa8622ae1c436c891132b6ff --- /dev/null +++ b/ecmascript/builtins/builtins_symbol.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_symbol.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_symbol.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/symbol_table-inl.h" +#include "utils/bit_utils.h" + +namespace panda::ecmascript::builtins { +// prototype +// 19.4.3.1 +// constructor +JSTaggedValue BuiltinsSymbol::SymbolConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.If NewTarget is not undefined, throw a TypeError exception. + JSHandle key = GetCallArg(argv, 0); + JSHandle newTarget = GetNewTarget(argv); + if (!newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "SymbolConstructor: NewTarget is not undefined", + JSTaggedValue::Exception()); + } + // 2.If description is undefined, let descString be undefined. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (key->IsUndefined()) { + JSHandle jsSymbol = factory->NewJSSymbol(); + return jsSymbol.GetTaggedValue(); + } + // 3.Else, let descString be ToString(description). + JSHandle descHandle = JSHandle::Cast(JSTaggedValue::ToString(thread, key)); + // 4.ReturnIfAbrupt(descString). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + // 5.Return a new unique Symbol value whose [[Description]] value is descString. + JSHandle jsSymbol = factory->NewPublicSymbol(descHandle); + return jsSymbol.GetTaggedValue(); +} + +// 19.4.3.2 Symbol.prototype.toString() +JSTaggedValue BuiltinsSymbol::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.Let s be the this value. + JSHandle valueHandle = GetThis(argv); + // 2.If Type(s) is Symbol, let sym be s. + JSTaggedValue sym = valueHandle.GetTaggedValue(); + // 3.Else + if (!valueHandle->IsSymbol()) { + if (valueHandle->IsObject()) { + if (!valueHandle->IsJSPrimitiveRef()) { + // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "ToString: no [[SymbolData]]", JSTaggedValue::Exception()); + } + // Let sym be the value of s's [[SymbolData]] internal slot. + JSTaggedValue primitive = JSPrimitiveRef::Cast(valueHandle->GetTaggedObject())->GetValue(); + ASSERT(primitive.IsSymbol()); + sym = primitive; + } else { + // If Type(s) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "ToString: s is not Object", JSTaggedValue::Exception()); + } + } + // Return SymbolDescriptiveString(sym). + return SymbolDescriptiveString(thread, sym); +} + +JSTaggedValue BuiltinsSymbol::SymbolDescriptiveString(JSThread *thread, JSTaggedValue sym) +{ + BUILTINS_API_TRACE(thread, Symbol, SymbolDescriptiveString); + // Assert: Type(sym) is Symbol. + ASSERT(sym.IsSymbol()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // Let desc be sym’s [[Description]] value. + auto symbolObject = reinterpret_cast(sym.GetTaggedObject()); + JSHandle descHandle(thread, symbolObject->GetDescription()); + + // If desc is undefined, let desc be the empty string. + + if (descHandle->IsUndefined()) { + JSHandle leftHandle(factory->NewFromString("Symbol(")); + JSHandle rightHandle(factory->NewFromString(")")); + JSHandle str = factory->ConcatFromString(leftHandle, rightHandle); + return str.GetTaggedValue(); + } + // Assert: Type(desc) is String. + ASSERT(descHandle->IsString()); + // Return the result of concatenating the strings "Symbol(", desc, and ")". + JSHandle leftHandle(factory->NewFromString("Symbol(")); + JSHandle rightHandle(factory->NewFromString(")")); + JSHandle stringLeft = + factory->ConcatFromString(leftHandle, JSTaggedValue::ToString(thread, descHandle)); + JSHandle str = factory->ConcatFromString(stringLeft, rightHandle); + return str.GetTaggedValue(); +} + +// 19.4.3.3 +JSTaggedValue BuiltinsSymbol::ValueOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, ValueOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // Let s be the this value. + JSHandle valueHandle = GetThis(argv); + // If Type(s) is Symbol, return s. + if (valueHandle->IsSymbol()) { + return valueHandle.GetTaggedValue(); + } + // If Type(s) is not Object, throw a TypeError exception. + if (!valueHandle->IsObject()) { + // return TypeError + THROW_TYPE_ERROR_AND_RETURN(thread, "ValueOf: s is not Object", JSTaggedValue::Exception()); + } + // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + if (!valueHandle->IsJSPrimitiveRef()) { + // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "ValueOf: no [[SymbolData]]", JSTaggedValue::Exception()); + } + JSTaggedValue primitive = JSPrimitiveRef::Cast(valueHandle->GetTaggedObject())->GetValue(); + ASSERT(primitive.IsSymbol()); + // Return the value of s's [[SymbolData]] internal slot. + return primitive; +} + +// 19.4.2.1 Symbol.for (key) +JSTaggedValue BuiltinsSymbol::For(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, For); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.Let stringKey be ToString(key). + JSHandle key = BuiltinsSymbol::GetCallArg(argv, 0); + JSHandle stringHandle = JSHandle::Cast(JSTaggedValue::ToString(thread, key)); + // 2.ReturnIfAbrupt + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + + // 3.For each element e of the GlobalSymbolRegistry List, + // If SameValue(e.[[key]], stringKey) is true, return e.[[symbol]]. + // 4.Assert: GlobalSymbolRegistry does not currently contain an entry for stringKey. + // 5.Let newSymbol be a new unique Symbol value whose [[Description]] value is stringKey. + // 6.Append the record { [[key]]: stringKey, [[symbol]]: newSymbol } to the GlobalSymbolRegistry List. + // 7.Return newSymbol. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle symbol = factory->NewSymbolWithTable(stringHandle); + return symbol.GetTaggedValue(); +} + +// 19.4.2.5 Symbol.keyFor (sym) +JSTaggedValue BuiltinsSymbol::KeyFor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, KeyFor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.If Type(sym) is not Symbol, throw a TypeError exception. + JSHandle sym = BuiltinsSymbol::GetCallArg(argv, 0); + if (!sym->IsSymbol()) { + // return typeError + THROW_TYPE_ERROR_AND_RETURN(thread, "KeyFor: sym is not Symbol", JSTaggedValue::Exception()); + } + // 2.For each element e of the GlobalSymbolRegistry List, + // If SameValue(e.[[symbol]], sym) is true, return e.[[key]]. + // 3.Assert: GlobalSymbolRegistry does not currently contain an entry for sym. + // 4.Return undefined. + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + auto *table = env->GetRegisterSymbols().GetObject(); + JSTaggedValue key = table->FindSymbol(thread, sym.GetTaggedValue()); + if (key.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + return JSTaggedValue::ToString(thread, JSHandle(thread, key)).GetTaggedValue(); +} + +// 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint ) +JSTaggedValue BuiltinsSymbol::ToPrimitive(EcmaRuntimeCallInfo *argv) +{ + // The allowed values for hint are "default", "number", and "string". + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, ToPrimitive); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.Let s be the this value. + JSHandle sym = GetThis(argv); + // 2.If Type(s) is Symbol, return s. + if (sym->IsSymbol()) { + return sym.GetTaggedValue(); + } + // 3.If Type(s) is not Object, throw a TypeError exception. + if (!sym->IsObject()) { + // return TypeError + THROW_TYPE_ERROR_AND_RETURN(thread, "ToPrimitive: s is not Object", JSTaggedValue::Exception()); + } + ASSERT(sym->IsObject()); + // 4.If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + // 5.Return the value of s's [[SymbolData]] internal slot. + if (!sym->IsJSPrimitiveRef()) { + // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "ToPrimitive: no [[SymbolData]]", JSTaggedValue::Exception()); + } + // Let sym be the value of s's [[SymbolData]] internal slot. + JSTaggedValue primitive = JSPrimitiveRef::Cast(sym->GetTaggedObject())->GetValue(); + ASSERT(primitive.IsSymbol()); + return primitive; +} + +JSTaggedValue BuiltinsSymbol::DescriptionGetter(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, DescriptionGetter); + // 1.Let s be the this value. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle s = GetThis(argv); + // 2.Let sym be ? thisSymbolValue(s). + // 3.Return sym.[[Description]]. + return ThisSymbolValue(thread, s); +} + +JSTaggedValue BuiltinsSymbol::ThisSymbolValue(JSThread *thread, const JSHandle &value) +{ + BUILTINS_API_TRACE(thread, Symbol, ThisSymbolValue); + if (value->IsSymbol()) { + JSTaggedValue desValue = JSSymbol::Cast(value->GetTaggedObject())->GetDescription(); + return desValue; + } + + // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + if (!value->IsJSPrimitive()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "ThisSymbolValue: no SymbolData", JSTaggedValue::Exception()); + } + JSTaggedValue primitive = JSPrimitiveRef::Cast(value->GetTaggedObject())->GetValue(); + ASSERT(primitive.IsSymbol()); + // Return the value of s's [[SymbolData]] internal slot. + JSTaggedValue primitiveDesValue = JSSymbol::Cast(primitive.GetTaggedObject())->GetDescription(); + return primitiveDesValue; +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_symbol.h b/ecmascript/builtins/builtins_symbol.h new file mode 100644 index 0000000000000000000000000000000000000000..8f22b62b2744be9d9c68c0ec76df129de341ae65 --- /dev/null +++ b/ecmascript/builtins/builtins_symbol.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_SYMBOL_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_SYMBOL_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript::builtins { +class BuiltinsSymbol : public base::BuiltinsBase { +public: + // 19.4.1 + static JSTaggedValue SymbolConstructor(EcmaRuntimeCallInfo *argv); + + // prototype + // 19.4.3.2 + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 19.4.3.3 + static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv); + // 19.4.2.1 Symbol.for (key) + static JSTaggedValue For(EcmaRuntimeCallInfo *argv); + // 19.4.2.5 Symbol.keyFor (sym) + static JSTaggedValue KeyFor(EcmaRuntimeCallInfo *argv); + + // 19.4.3.2 get Symbol.prototype.description + static JSTaggedValue DescriptionGetter(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ThisSymbolValue(JSThread *thread, const JSHandle &value); + + // 19.4.3.4 + static JSTaggedValue ToPrimitive(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue SymbolDescriptiveString(JSThread *thread, JSTaggedValue sym); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_SYMBOL_H diff --git a/ecmascript/builtins/builtins_typedarray.cpp b/ecmascript/builtins/builtins_typedarray.cpp new file mode 100644 index 0000000000000000000000000000000000000000..24718e3e48bb83510cce28f3f4b451e094c1ba8d --- /dev/null +++ b/ecmascript/builtins/builtins_typedarray.cpp @@ -0,0 +1,1382 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_typedarray.h" +#include +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/base/typed_array_helper.h" +#include "ecmascript/builtins/builtins_array.h" +#include "ecmascript/builtins/builtins_arraybuffer.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript::builtins { +using TypedArrayHelper = base::TypedArrayHelper; +using BuiltinsArray = builtins::BuiltinsArray; +using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; + +// 22.2.1 +JSTaggedValue BuiltinsTypedArray::TypedArrayBaseConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, BaseConstructor); + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "TypedArray Constructor cannot be called.", + JSTaggedValue::Exception()); +} + +JSTaggedValue BuiltinsTypedArray::Int8ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledInt8ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Uint8ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledUint8ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Uint8ClampedArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, + thread->GlobalConstants()->GetHandledUint8ClampedArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Int16ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledInt16ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Uint16ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledUint16ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Int32ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledInt32ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Uint32ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledUint32ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Float32ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledFloat32ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Float64ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledFloat64ArrayString()); +} + +// 22.2.2.1 %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] ) +JSTaggedValue BuiltinsTypedArray::From(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, From); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // 1. Let C be the this value. + // 2. If IsConstructor(C) is false, throw a TypeError exception. + JSHandle thisHandle = GetThis(argv); + if (!thisHandle->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the this value is not a Constructor.", JSTaggedValue::Exception()); + } + + JSHandle thisArgHandle = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + // 3. If mapfn is undefined, let mapping be false. + // 4. Else, + // a. If IsCallable(mapfn) is false, throw a TypeError exception. + // b. Let mapping be true. + bool mapping = false; + JSHandle mapfn = GetCallArg(argv, 1); + if (!mapfn->IsUndefined()) { + if (!mapfn->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the mapfn is not callable.", JSTaggedValue::Exception()); + } + mapping = true; + } + // 5. Let usingIterator be ? GetMethod(source, @@iterator). + JSHandle source = GetCallArg(argv, 0); + if (!source->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the source is not an object.", JSTaggedValue::Exception()); + } + JSHandle sourceObj(source); + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle usingIterator = + JSObject::GetMethod(thread, JSHandle::Cast(sourceObj), iteratorSymbol); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. If usingIterator is not undefined, then + // a. Let values be ? IterableToList(source, usingIterator). + // b. Let len be the number of elements in values. + // c. Let targetObj be ? TypedArrayCreate(C, « len »). + array_size_t length = 2; + JSHandle msg = factory->NewTaggedArray(length); + if (!usingIterator->IsUndefined()) { + CVector> vec; + JSHandle iterator = JSIterator::GetIterator(thread, source, usingIterator); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle next(thread, JSTaggedValue::True()); + while (!next->IsFalse()) { + next = JSIterator::IteratorStep(thread, iterator); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!next->IsFalse()) { + JSHandle nextValue = JSIterator::IteratorValue(thread, next); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, nextValue.GetTaggedValue()); + vec.push_back(nextValue); + } + } + int32_t len = vec.size(); + array_size_t arrayLength = 1; + JSHandle array = factory->NewTaggedArray(arrayLength); + array->Set(thread, 0, JSTaggedValue(len)); + JSHandle targetObj = TypedArrayHelper::TypedArrayCreate(thread, thisHandle, array); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // d. Let k be 0. + // e. Repeat, while k < len + // i. Let Pk be ! ToString(k). + // ii. Let kValue be the first element of values and remove that element from values. + // iii. If mapping is true, then + // 1. Let mappedValue be ? Call(mapfn, thisArg, « kValue, k »). + // iv. Else, let mappedValue be kValue. + // v. Perform ? Set(targetObj, Pk, mappedValue, true). + // vi. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle mapValue(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < len) { + JSHandle kValue = vec[k]; + if (mapping) { + msg->Set(thread, 0, kValue); + msg->Set(thread, 1, JSTaggedValue(k)); + JSTaggedValue callResult = JSFunction::Call(thread, mapfn, thisArgHandle, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + mapValue.Update(callResult); + } else { + mapValue.Update(kValue.GetTaggedValue()); + } + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(targetObj), kKey, mapValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // f. Assert: values is now an empty List. + // g. Return targetObj. + return targetObj.GetTaggedValue(); + } + + // 7. NOTE: source is not an Iterable so assume it is already an array-like object. + // 8. Let arrayLike be ! ToObject(source). + JSHandle arrayLikeObj = JSTaggedValue::ToObject(thread, source); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle arrayLike(arrayLikeObj); + // 9. Let len be ? LengthOfArrayLike(arrayLike). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle lenResult = JSTaggedValue::GetProperty(thread, arrayLike, lengthKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedNumber tLen = JSTaggedValue::ToLength(thread, lenResult); + // 6. ReturnIfAbrupt(relativeTarget). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double len = tLen.GetNumber(); + + array_size_t arrayLen = 1; + // 10. Let targetObj be ? TypedArrayCreate(C, « len »). + JSHandle array = factory->NewTaggedArray(arrayLen); + array->Set(thread, 0, JSTaggedValue(len)); + JSHandle targetObj = TypedArrayHelper::TypedArrayCreate(thread, thisHandle, array); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11. Let k be 0. + // 12. Repeat, while k < len + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(arrayLike, Pk). + // c. If mapping is true, then + // i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, k »). + // d. Else, let mappedValue be kValue. + // e. Perform ? Set(targetObj, Pk, mappedValue, true). + // f. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < len) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSHandle kValue = JSTaggedValue::GetProperty(thread, arrayLike, kKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle mapValue; + if (mapping) { + msg->Set(thread, 0, kValue); + msg->Set(thread, 1, JSTaggedValue(k)); + JSTaggedValue callResult = JSFunction::Call(thread, mapfn, thisArgHandle, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + mapValue = JSHandle(thread, callResult); + } else { + mapValue = kValue; + } + JSTaggedValue::SetProperty(thread, JSHandle::Cast(targetObj), kKey, mapValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // 13. Return targetObj. + return targetObj.GetTaggedValue(); +} + +// 22.2.2.2 %TypedArray%.of ( ...items ) +JSTaggedValue BuiltinsTypedArray::Of(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Of); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let len be the actual number of arguments passed to this function. + array_size_t len = argv->GetArgsNumber(); + // 2. Let items be the List of arguments passed to this function. + // 3. Let C be the this value. + JSHandle thisHandle = GetThis(argv); + // 4. If IsConstructor(C) is false, throw a TypeError exception. + if (!thisHandle->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the this value is not a Constructor.", JSTaggedValue::Exception()); + } + // 5. Let newObj be TypedArrayCreate(C, « len »). + array_size_t length = 1; + JSHandle array = factory->NewTaggedArray(length); + array->Set(thread, 0, JSTaggedValue(len)); + JSHandle newObj = TypedArrayHelper::TypedArrayCreate(thread, thisHandle, array); + // 6. ReturnIfAbrupt(newObj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let kValue be items[k]. + // b. Let Pk be ! ToString(k). + // c. Perform ? Set(newObj, Pk, kValue, true). + // d. ReturnIfAbrupt(status). + // e. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < len) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSHandle kValue = GetCallArg(argv, k); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newObj), kKey, kValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // 9. Return newObj. + return newObj.GetTaggedValue(); +} + +// 22.2.2.4 +JSTaggedValue BuiltinsTypedArray::Species(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // 1. Return the this value. + return GetThis(argv).GetTaggedValue(); +} + +// prototype +// 22.2.3.1 get %TypedArray%.prototype.buffer +JSTaggedValue BuiltinsTypedArray::GetBuffer(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, GetBuffer); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[ViewedArrayBuffer]] internal slot.", + JSTaggedValue::Exception()); + } + // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = TypedArrayHelper::GetViewedArrayBuffer(JSHandle(thisHandle)); + // 5. Return buffer. + return buffer; +} + +// 22.2.3.2 +JSTaggedValue BuiltinsTypedArray::GetByteLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, GetByteLength); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[ViewedArrayBuffer]] internal slot.", + JSTaggedValue::Exception()); + } + // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = TypedArrayHelper::GetViewedArrayBuffer(JSHandle(thisHandle)); + // 5. If IsDetachedBuffer(buffer) is true, return 0. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + return JSTaggedValue(0); + } + // 6. Let size be the value of O’s [[ByteLength]] internal slot. + int32_t size = TypedArrayHelper::GetByteLength(thread, JSHandle(thisHandle)); + // 7. Return size. + return JSTaggedValue(size); +} + +// 22.2.3.3 +JSTaggedValue BuiltinsTypedArray::GetByteOffset(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, GetByteOffset); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[ViewedArrayBuffer]] internal slot.", + JSTaggedValue::Exception()); + } + // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = TypedArrayHelper::GetViewedArrayBuffer(JSHandle(thisHandle)); + // 5. If IsDetachedBuffer(buffer) is true, return 0. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + return JSTaggedValue(0); + } + // 6. Let offset be the value of O’s [[ByteOffset]] internal slot. + int32_t offset = TypedArrayHelper::GetByteOffset(thread, JSHandle(thisHandle)); + // 7. Return offset. + return JSTaggedValue(offset); +} + +// 22.2.3.5 +JSTaggedValue BuiltinsTypedArray::CopyWithin(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, CopyWithin); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::CopyWithin(argv); +} + +// 22.2.3.6 +JSTaggedValue BuiltinsTypedArray::Entries(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Entries); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Let valid be ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // 3. ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + JSHandle self(thisHandle); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 4. Return CreateArrayIterator(O, "key+value"). + JSHandle iter(factory->NewJSArrayIterator(self, IterationKind::KEY_AND_VALUE)); + return iter.GetTaggedValue(); +} + +// 22.2.3.7 +JSTaggedValue BuiltinsTypedArray::Every(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Every); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + int32_t len = TypedArrayHelper::GetArrayLength(thread, thisObjHandle); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let testResult be ToBoolean(Call(callbackfn, T, «kValue, k, O»)). + // iv. ReturnIfAbrupt(testResult). + // v. If testResult is false, return false. + // e. Increase k by 1. + array_size_t length = 3; + JSHandle msg = factory->NewTaggedArray(length); + int32_t k = 0; + while (k < len) { + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle kValue = JSTaggedValue::GetProperty(thread, thisObjVal, k).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, 0, kValue); + msg->Set(thread, 1, JSTaggedValue(k)); + msg->Set(thread, INDEX_TWO, thisObjVal); + JSTaggedValue callResult = JSFunction::Call(thread, callbackFnHandle, thisArgHandle, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool boolResult = callResult.ToBoolean(); + if (!boolResult) { + return GetTaggedBoolean(false); + } + k++; + } + + // 9. Return true. + return GetTaggedBoolean(true); +} + +// 22.2.3.8 +JSTaggedValue BuiltinsTypedArray::Fill(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::Fill(argv); +} + +// 22.2.3.9 %TypedArray%.prototype.filter ( callbackfn [ , thisArg ] ) +JSTaggedValue BuiltinsTypedArray::Filter(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Filter); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Let valid be ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // 3. ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle thisObj(thisHandle); + // 4. Let len be the value of O’s [[ArrayLength]] internal slot. + int32_t len = TypedArrayHelper::GetArrayLength(thread, thisObj); + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackfnHandle = GetCallArg(argv, 0); + if (!callbackfnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 10. Let kept be a new empty List. + JSHandle kept(factory->NewTaggedArray(len)); + + // 11. Let k be 0. + // 12. Let captured be 0. + // 13. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kValue be Get(O, Pk). + // c. ReturnIfAbrupt(kValue). + // d. Let selected be ToBoolean(Call(callbackfn, T, «kValue, k, O»)). + // e. ReturnIfAbrupt(selected). + // f. If selected is true, then + // i. Append kValue to the end of kept. + // ii. Increase captured by 1. + // g. Increase k by 1. + int32_t capt = 0; + array_size_t length = 3; + JSHandle msg = factory->NewTaggedArray(length); + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + for (int32_t k = 0; k < len; k++) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSHandle kValue = JSTaggedValue::GetProperty(thread, thisHandle, kKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, 0, kValue); + msg->Set(thread, 1, JSTaggedValue(k)); + msg->Set(thread, 2, thisHandle); // 2: the third arg is this + JSTaggedValue callResult = JSFunction::Call(thread, callbackfnHandle, thisArgHandle, msg); + bool testResult = callResult.ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (testResult) { + kept->Set(thread, capt, kValue); + capt++; + } + } + // es11 9. Let A be ? TypedArraySpeciesCreate(O, « captured »). + JSHandle argumentsList = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1); + argumentsList->Set(thread, 0, JSTaggedValue(capt)); + JSHandle newArrObj = TypedArrayHelper::TypedArraySpeciesCreate(thread, thisObj, argumentsList); + // 15. ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 16. Let n be 0. + // 17. For each element e of kept + // a. Let status be Set(A, ToString(n), e, true ). + // b. ReturnIfAbrupt(status). + // c. Increment n by 1. + JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); + JSMutableHandle ntKey(thread, JSTaggedValue::Undefined()); + for (int32_t n = 0; n < capt; n++) { + valueHandle.Update(kept->Get(n)); + ntKey.Update(JSTaggedValue(n)); + JSHandle nKey(JSTaggedValue::ToString(thread, ntKey)); + JSTaggedValue::SetProperty(thread, JSHandle(newArrObj), nKey, valueHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 18. Return A. + return newArrObj.GetTaggedValue(); +} + +// 22.2.3.10 +JSTaggedValue BuiltinsTypedArray::Find(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::Find(argv); +} + +// 22.2.3.11 +JSTaggedValue BuiltinsTypedArray::FindIndex(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::FindIndex(argv); +} + +// 22.2.3.12 +JSTaggedValue BuiltinsTypedArray::ForEach(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, ForEach); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + int32_t len = TypedArrayHelper::GetArrayLength(thread, thisObjHandle); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»). + // iv. ReturnIfAbrupt(funcResult). + // e. Increase k by 1. + array_size_t length = 3; + JSHandle msg = factory->NewTaggedArray(length); + int32_t k = 0; + while (k < len) { + JSHandle kValue = JSTaggedValue::GetProperty(thread, thisObjVal, k).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, 0, kValue); + msg->Set(thread, 1, JSTaggedValue(k)); + msg->Set(thread, INDEX_TWO, thisObjVal); + JSTaggedValue funcResult = JSFunction::Call(thread, callbackFnHandle, thisArgHandle, msg); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); + k++; + } + + // 9. Return undefined. + return JSTaggedValue::Undefined(); +} + +// 22.2.3.13 +JSTaggedValue BuiltinsTypedArray::IndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::IndexOf(argv); +} + +// 22.2.3.14 +JSTaggedValue BuiltinsTypedArray::Join(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::Join(argv); +} + +// 22.2.3.15 +JSTaggedValue BuiltinsTypedArray::Keys(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Keys); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Let valid be ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // 3. ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + JSHandle self(thisHandle); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 4. Return CreateArrayIterator(O, "key"). + JSHandle iter(factory->NewJSArrayIterator(self, IterationKind::KEY)); + return iter.GetTaggedValue(); +} + +// 22.2.3.16 +JSTaggedValue BuiltinsTypedArray::LastIndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::LastIndexOf(argv); +} + +// 22.2.3.17 +JSTaggedValue BuiltinsTypedArray::GetLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, GetLength); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[TypedArrayName]] internal slot, throw a TypeError exception. + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[TypedArrayName]] internal slot.", + JSTaggedValue::Exception()); + } + // 4. Assert: O has [[ViewedArrayBuffer]] and [[ArrayLength]] internal slots. + // 5. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = TypedArrayHelper::GetViewedArrayBuffer(JSHandle(thisHandle)); + // 6. If IsDetachedBuffer(buffer) is true, return 0. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + return JSTaggedValue(0); + } + // 7. Let length be the value of O’s [[ArrayLength]] internal slot. + int32_t length = TypedArrayHelper::GetArrayLength(thread, JSHandle(thisHandle)); + // 8. Return length. + return JSTaggedValue(length); +} + +// 22.2.3.18 %TypedArray%.prototype.map ( callbackfn [ , thisArg ] ) +JSTaggedValue BuiltinsTypedArray::Map(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Map); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Let valid be ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // 3. ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle thisObj(thisHandle); + // 4. Let len be the value of O’s [[ArrayLength]] internal slot. + int32_t len = TypedArrayHelper::GetArrayLength(thread, thisObj); + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackfnHandle = GetCallArg(argv, 0); + if (!callbackfnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + // es11 5. Let A be ? TypedArraySpeciesCreate(O, « len »). + JSHandle argumentsList = factory->NewTaggedArray(1); + argumentsList->Set(thread, 0, JSTaggedValue(len)); + JSHandle newArrObj = TypedArrayHelper::TypedArraySpeciesCreate(thread, thisObj, argumentsList); + // 11. ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 12. Let k be 0. + // 13. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kValue be Get(O, Pk). + // c. ReturnIfAbrupt(kValue). + // d. Let mappedValue be Call(callbackfn, T, «kValue, k, O»). + // e. ReturnIfAbrupt(mappedValue). + // f. Let status be Set(A, Pk, mappedValue, true ). + // g. ReturnIfAbrupt(status). + // h. Increase k by 1. + array_size_t length = 3; + JSHandle msg = factory->NewTaggedArray(length); + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle kValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle mapValue(thread, JSTaggedValue::Undefined()); + for (int32_t k = 0; k < len; k++) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + kValue.Update(JSTaggedValue::GetProperty(thread, thisHandle, kKey).GetValue().GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + msg->Set(thread, ArgsPosition::FIRST, kValue); + msg->Set(thread, ArgsPosition::SECOND, JSTaggedValue(k)); + msg->Set(thread, ArgsPosition::THIRD, thisHandle); + JSTaggedValue callResult = JSFunction::Call(thread, callbackfnHandle, thisArgHandle, msg); + mapValue.Update(callResult); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::SetProperty(thread, JSHandle(newArrObj), kKey, mapValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + // 14. Return A. + return newArrObj.GetTaggedValue(); +} + +// 22.2.3.19 +JSTaggedValue BuiltinsTypedArray::Reduce(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::Reduce(argv); +} + +// 22.2.3.20 +JSTaggedValue BuiltinsTypedArray::ReduceRight(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::ReduceRight(argv); +} + +// 22.2.3.21 +JSTaggedValue BuiltinsTypedArray::Reverse(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::Reverse(argv); +} + +// 22.2.3.22 %TypedArray%.prototype.set ( overloaded [ , offset ]) +JSTaggedValue BuiltinsTypedArray::Set(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Set); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // 1. Assert: array is any ECMAScript language value other than an Object with a [[TypedArrayName]] internal slot. + // If it is such an Object, the definition in 22.2.3.22.2 applies. + // 2. Let target be the this value. + JSHandle target = GetThis(argv); + // 3. If Type(target) is not Object, throw a TypeError exception. + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); + } + JSHandle targetObj(target); + // 4. If target does not have a [[TypedArrayName]] internal slot, throw a TypeError exception. + if (!target->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[TypedArrayName]] internal slot.", + JSTaggedValue::Exception()); + } + + // 5. Assert: target has a [[ViewedArrayBuffer]] internal slot. + // 6. Let targetOffset be ToInteger (offset). + JSTaggedNumber tTargetOffset = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 1)); + // 7. ReturnIfAbrupt(targetOffset). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double targetOffset = tTargetOffset.GetNumber(); + // 8. If targetOffset < 0, throw a RangeError exception. + if (targetOffset < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The targetOffset of This value is less than 0.", + JSTaggedValue::Exception()); + } + // 9. Let targetBuffer be the value of target’s [[ViewedArrayBuffer]] internal slot. + JSHandle targetBuffer(thread, TypedArrayHelper::GetViewedArrayBuffer(targetObj)); + // 10. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(targetBuffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The targetBuffer of This value is detached buffer.", + JSTaggedValue::Exception()); + } + // 11. Let targetLength be the value of target’s [[ArrayLength]] internal slot. + // 12. Let targetName be the String value of target’s [[TypedArrayName]] internal slot. + // 13. Let targetElementSize be the Number value of the Element Size value specified in Table 49 for targetName. + // 14. Let targetType be the String value of the Element Type value in Table 49 for targetName. + // 15. Let targetByteOffset be the value of target’s [[ByteOffset]] internal slot. + int32_t targetLength = TypedArrayHelper::GetArrayLength(thread, targetObj); + JSHandle targetName = TypedArrayHelper::GetTypedArrayName(thread, targetObj); + int32_t targetElementSize = TypedArrayHelper::GetSizeFromName(thread, targetName); + DataViewType targetType = TypedArrayHelper::GetTypeFromName(thread, targetName); + int32_t targetByteOffset = TypedArrayHelper::GetByteOffset(thread, targetObj); + + JSHandle argArray = GetCallArg(argv, 0); + + // 22.2.3.22.1 %TypedArray%.prototype.set (array [ , offset ] ) + if (!argArray->IsTypedArray()) { + // 16. Let src be ToObject(array). + JSHandle src = JSTaggedValue::ToObject(thread, argArray); + // 17. ReturnIfAbrupt(src). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 18. Let srcLength be ToLength(Get(src, "length")). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle lenResult = + JSTaggedValue::GetProperty(thread, JSHandle::Cast(src), lengthKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedNumber tSrcLen = JSTaggedValue::ToLength(thread, lenResult); + // 19. ReturnIfAbrupt(srcLength). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double srcLen = tSrcLen.GetNumber(); + // 20. If srcLength + targetOffset > targetLength, throw a RangeError exception. + if (srcLen + targetOffset > targetLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The sum of srcLength and targetOffset is greater than targetLength.", + JSTaggedValue::Exception()); + } + // 21. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset. + int32_t targetByteIndex = targetOffset * targetElementSize + targetByteOffset; + // 22. Let k be 0. + // 23. Let limit be targetByteIndex + targetElementSize × srcLength. + int32_t k = 0; + int32_t limit = targetByteIndex + targetElementSize * srcLen; + // 24. Repeat, while targetByteIndex < limit + // a. Let Pk be ToString(k). + // b. Let kNumber be ToNumber(Get(src, Pk)). + // c. ReturnIfAbrupt(kNumber). + // d. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. + // e. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, kNumber). + // f. Set k to k + 1. + // g. Set targetByteIndex to targetByteIndex + targetElementSize. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + while (targetByteIndex < limit) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSHandle kValue = + JSTaggedValue::GetProperty(thread, JSHandle::Cast(src), kKey).GetValue(); + JSTaggedNumber kNumber = JSTaggedValue::ToNumber(thread, kValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (BuiltinsArrayBuffer::IsDetachedBuffer(targetBuffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The targetBuffer of This value is detached buffer.", + JSTaggedValue::Exception()); + } + BuiltinsArrayBuffer::SetValueInBuffer(targetBuffer.GetTaggedValue(), targetByteIndex, targetType, kNumber, + true); + k++; + targetByteIndex = targetByteIndex + targetElementSize; + } + // 25. Return undefined. + return JSTaggedValue::Undefined(); + } + + // 22.2.3.22.2 %TypedArray%.prototype.set(typedArray [, offset ] ) + JSHandle typedArray(argArray); + // 12. Let srcBuffer be the value of typedArray’s [[ViewedArrayBuffer]] internal slot. + // 13. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + JSTaggedValue srcBuffer = TypedArrayHelper::GetViewedArrayBuffer(typedArray); + if (BuiltinsArrayBuffer::IsDetachedBuffer(srcBuffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The ArrayBuffer of typedArray is detached buffer.", + JSTaggedValue::Exception()); + } + // 18. Let srcName be the String value of typedArray’s [[TypedArrayName]] internal slot. + // 19. Let srcType be the String value of the Element Type value in Table 49 for srcName . + // 20. Let srcElementSize be the Number value of the Element Size value specified in Table 49 for srcName. + // 21. Let srcLength be the value of typedArray’s [[ArrayLength]] internal slot. + // 22. Let srcByteOffset be the value of typedArray’s [[ByteOffset]] internal slot. + JSHandle srcName = TypedArrayHelper::GetTypedArrayName(thread, typedArray); + DataViewType srcType = TypedArrayHelper::GetTypeFromName(thread, srcName); + int32_t srcElementSize = TypedArrayHelper::GetSizeFromName(thread, srcName); + int32_t srcLength = TypedArrayHelper::GetArrayLength(thread, typedArray); + int32_t srcByteOffset = TypedArrayHelper::GetByteOffset(thread, typedArray); + // 23. If srcLength + targetOffset > targetLength, throw a RangeError exception. + if (srcLength + targetOffset > targetLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The sum of srcLength and targetOffset is greater than targetLength.", + JSTaggedValue::Exception()); + } + // 24. If SameValue(srcBuffer, targetBuffer) is true, then + // a. Let srcBuffer be CloneArrayBuffer(targetBuffer, srcByteOffset, %ArrayBuffer%). + // b. NOTE: %ArrayBuffer% is used to clone targetBuffer because is it known to not have any observable + // side-effects. + // c. ReturnIfAbrupt(srcBuffer). + // d. Let srcByteIndex be 0. + // 25. Else, let srcByteIndex be srcByteOffset. + int32_t srcByteIndex; + if (JSTaggedValue::SameValue(srcBuffer, targetBuffer.GetTaggedValue())) { + srcBuffer = + BuiltinsArrayBuffer::CloneArrayBuffer(thread, targetBuffer, srcByteOffset, env->GetArrayBufferFunction()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + srcByteIndex = 0; + } else { + srcByteIndex = srcByteOffset; + } + // 26. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset. + int32_t targetByteIndex = targetOffset * targetElementSize + targetByteOffset; + // 27. Let limit be targetByteIndex + targetElementSize × srcLength. + int32_t limit = targetByteIndex + targetElementSize * srcLength; + // 28. If SameValue(srcType, targetType) is false, then + // a. Repeat, while targetByteIndex < limit + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType). + // ii. Perform SetValueInBuffer (targetBuffer, targetByteIndex, targetType, value). + // iii. Set srcByteIndex to srcByteIndex + srcElementSize. + // iv. Set targetByteIndex to targetByteIndex + targetElementSize. + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + if (srcType != targetType) { + while (targetByteIndex < limit) { + JSTaggedValue taggedData = BuiltinsArrayBuffer::GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true); + value.Update(taggedData); + JSTaggedNumber kNumber = JSTaggedValue::ToNumber(thread, value); + BuiltinsArrayBuffer::SetValueInBuffer(targetBuffer.GetTaggedValue(), targetByteIndex, targetType, kNumber, + true); + srcByteIndex = srcByteIndex + srcElementSize; + targetByteIndex = targetByteIndex + targetElementSize; + } + } else { + // 29. Else, + // a. NOTE: If srcType and targetType are the same the transfer must be performed in a manner that preserves + // the bit-level encoding of the source data. + // b. Repeat, while targetByteIndex < limit + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, "Uint8"). + // ii. Perform SetValueInBuffer (targetBuffer, targetByteIndex, "Uint8", value). + // iii. Set srcByteIndex to srcByteIndex + 1. + // iv. Set targetByteIndex to targetByteIndex + 1. + while (targetByteIndex < limit) { + JSTaggedValue taggedData = + BuiltinsArrayBuffer::GetValueFromBuffer(srcBuffer, srcByteIndex, DataViewType::UINT8, true); + value.Update(taggedData); + JSTaggedNumber kNumber = JSTaggedValue::ToNumber(thread, value); + BuiltinsArrayBuffer::SetValueInBuffer(targetBuffer.GetTaggedValue(), targetByteIndex, DataViewType::UINT8, + kNumber, true); + srcByteIndex = srcByteIndex + 1; + targetByteIndex = targetByteIndex + 1; + } + } + // 30. Return undefined. + return JSTaggedValue::Undefined(); +} // namespace panda::ecmascript::builtins + +// 22.2.3.23 %TypedArray%.prototype.slice ( start, end ) +JSTaggedValue BuiltinsTypedArray::Slice(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Slice); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Let valid be ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // 3. ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle thisObj(thisHandle); + // 4. Let len be the value of O’s [[ArrayLength]] internal slot. + int32_t len = TypedArrayHelper::GetArrayLength(thread, thisObj); + + double k; + // 5. Let relativeStart be ToInteger(start). + JSTaggedNumber tRelativeStart = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0)); + // 6. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double relativeStart = tRelativeStart.GetNumber(); + // 7. If relativeStart < 0, let k be max((len + relativeStart),0); else let k be min(relativeStart, len). + if (relativeStart < 0) { + k = relativeStart + len > 0 ? relativeStart + len : 0; + } else { + k = relativeStart < len ? relativeStart : len; + } + // 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end). + double relativeEnd = len; + JSHandle end = GetCallArg(argv, 1); + if (!end->IsUndefined()) { + JSTaggedNumber tRelativeEnd = JSTaggedValue::ToInteger(thread, end); + // 9. ReturnIfAbrupt(relativeEnd). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + relativeEnd = tRelativeEnd.GetNumber(); + } + + // 10. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len). + double final = 0; + if (relativeEnd < 0) { + final = relativeEnd + len > 0 ? relativeEnd + len : 0; + } else { + final = relativeEnd < len ? relativeEnd : len; + } + // 11. Let count be max(final – k, 0). + double count = (final - k) > 0 ? (final - k) : 0; + // es11 9. Let A be ? TypedArraySpeciesCreate(O, « count »). + JSHandle argumentsList = factory->NewTaggedArray(1); + argumentsList->Set(thread, 0, JSTaggedValue(count)); + JSHandle newArrObj = TypedArrayHelper::TypedArraySpeciesCreate(thread, thisObj, argumentsList); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 17. Let srcName be the String value of O’s [[TypedArrayName]] internal slot. + // 18. Let srcType be the String value of the Element Type value in Table 49 for srcName. + JSHandle srcName = TypedArrayHelper::GetTypedArrayName(thread, thisObj); + DataViewType srcType = TypedArrayHelper::GetTypeFromName(thread, srcName); + // 19. Let targetName be the String value of A’s [[TypedArrayName]] internal slot. + // 20. Let targetType be the String value of the Element Type value in Table 49 for targetName. + JSHandle targetName = TypedArrayHelper::GetTypedArrayName(thread, newArrObj); + DataViewType targetType = TypedArrayHelper::GetTypeFromName(thread, targetName); + // 21. If SameValue(srcType, targetType) is false, then + // a. Let n be 0. + // b. Repeat, while k < final + // i. Let Pk be ToString(k). + // ii. Let kValue be Get(O, Pk). + // iii. ReturnIfAbrupt(kValue). + // iv. Let status be Set(A, ToString(n), kValue, true ). + // v. ReturnIfAbrupt(status). + // vi. Increase k by 1. + // vii. Increase n by 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle kValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle ntKey(thread, JSTaggedValue::Undefined()); + if (srcType != targetType) { + double n = 0; + while (k < final) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + kValue.Update(JSTaggedValue::GetProperty(thread, thisHandle, kKey).GetValue().GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ntKey.Update(JSTaggedValue(n)); + JSHandle nKey(JSTaggedValue::ToString(thread, ntKey)); + JSTaggedValue::SetProperty(thread, JSHandle(newArrObj), nKey, kValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + n++; + k++; + } + } else if (count > 0) { + // 22. Else if count > 0, + // a. Let srcBuffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + // b. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + JSHandle srcBuffer(thread, TypedArrayHelper::GetViewedArrayBuffer(thisObj)); + if (BuiltinsArrayBuffer::IsDetachedBuffer(srcBuffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The ArrayBuffer of this value is detached buffer.", + JSTaggedValue::Exception()); + } + // c. Let targetBuffer be the value of A’s [[ViewedArrayBuffer]] internal slot. + JSHandle targetBuffer(thread, TypedArrayHelper::GetViewedArrayBuffer(newArrObj)); + // d. Let elementSize be the Number value of the Element Size value specified in Table 49 for srcType. + int32_t elementSize = TypedArrayHelper::GetSizeFromName(thread, srcName); + // e. NOTE: If srcType and targetType are the same the transfer must be performed in a manner that + // preserves the bit-level encoding of the source data. f. Let srcByteOffset be the value of O’s + // [[ByteOffset]] internal slot. + int32_t srcByteOffset = TypedArrayHelper::GetByteOffset(thread, thisObj); + // g. Let targetByteIndex be 0. + // h. Let srcByteIndex be (k × elementSize) + srcByteOffset. + + int32_t srcByteIndex = k * elementSize + srcByteOffset; + // i. Repeat, while targetByteIndex < count × elementSize + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, "Uint8"). + // ii. Perform SetValueInBuffer (targetBuffer, targetByteIndex, "Uint8", value). + // iii. Increase srcByteIndex by 1. + // iv. Increase targetByteIndex by 1. + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + for (int32_t targetByteIndex = 0; targetByteIndex < count * elementSize; srcByteIndex++, targetByteIndex++) { + JSTaggedValue taggedData = BuiltinsArrayBuffer::GetValueFromBuffer(srcBuffer.GetTaggedValue(), srcByteIndex, + DataViewType::UINT8, true); + value.Update(taggedData); + JSTaggedNumber kNumber = JSTaggedValue::ToNumber(thread, value); + BuiltinsArrayBuffer::SetValueInBuffer(targetBuffer.GetTaggedValue(), targetByteIndex, DataViewType::UINT8, + kNumber, true); + } + } + // 23. Return A. + return newArrObj.GetTaggedValue(); +} + +// 22.2.3.24 +JSTaggedValue BuiltinsTypedArray::Some(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::Some(argv); +} + +// 22.2.3.25 +JSTaggedValue BuiltinsTypedArray::Sort(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Sort); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let obj be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + JSHandle buffer; + buffer = JSHandle(thread, TypedArrayHelper::ValidateTypedArray(thread, thisHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double len = TypedArrayHelper::GetArrayLength(thread, thisObjHandle); + + JSHandle callbackFnHandle = GetCallArg(argv, 0); + + uint32_t i = 0; + while (i < len - 1) { + uint32_t j = len - 1; + while (j > i) { + JSHandle xValue = JSTaggedValue::GetProperty(thread, thisObjVal, j - 1).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle yValue = JSTaggedValue::GetProperty(thread, thisObjVal, j).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t compareResult; + compareResult = TypedArrayHelper::SortCompare(thread, callbackFnHandle, buffer, xValue, yValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (compareResult > 0) { + JSTaggedValue::SetProperty(thread, thisObjVal, j - 1, yValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::SetProperty(thread, thisObjVal, j, xValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + j--; + } + i++; + } + return JSTaggedValue::ToObject(thread, thisHandle).GetTaggedValue(); +} + +// 22.2.3.26 %TypedArray%.prototype.subarray( [ begin [ , end ] ] ) +JSTaggedValue BuiltinsTypedArray::Subarray(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Subarray); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); + } + JSHandle thisObj(thisHandle); + // 3. If O does not have a [[TypedArrayName]] internal slot, throw a TypeError exception. + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[TypedArrayName]] internal slot.", + JSTaggedValue::Exception()); + } + // 4. Assert: O has a [[ViewedArrayBuffer]] internal slot. + // 6. Let srcLength be the value of O’s [[ArrayLength]] internal slot. + int32_t srcLength = TypedArrayHelper::GetArrayLength(thread, thisObj); + // 7. Let relativeBegin be ToInteger(begin). + JSTaggedNumber tRelativeBegin = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0)); + // 8. ReturnIfAbrupt(relativeBegin). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double relativeBegin = tRelativeBegin.GetNumber(); + + double beginIndex; + // 9. If relativeBegin < 0, let beginIndex be max((srcLength + relativeBegin), 0); else let beginIndex be + // min(relativeBegin, srcLength). + if (relativeBegin < 0) { + beginIndex = relativeBegin + srcLength > 0 ? relativeBegin + srcLength : 0; + } else { + beginIndex = relativeBegin < srcLength ? relativeBegin : srcLength; + } + + // 10. If end is undefined, let relativeEnd be srcLength; else, let relativeEnd be ToInteger(end). + double relativeEnd = srcLength; + JSHandle end = GetCallArg(argv, 1); + if (!end->IsUndefined()) { + JSTaggedNumber tRelativeEnd = JSTaggedValue::ToInteger(thread, end); + // 11. ReturnIfAbrupt(relativeEnd). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + relativeEnd = tRelativeEnd.GetNumber(); + } + // 12. If relativeEnd < 0, let endIndex be max((srcLength + relativeEnd), 0); else let endIndex be + // min(relativeEnd, srcLength). + double endIndex; + if (relativeEnd < 0) { + endIndex = relativeEnd + srcLength > 0 ? relativeEnd + srcLength : 0; + } else { + endIndex = relativeEnd < srcLength ? relativeEnd : srcLength; + } + // 13. Let newLength be max(endIndex – beginIndex, 0). + double newLength = (endIndex - beginIndex) > 0 ? (endIndex - beginIndex) : 0; + // 14. Let constructorName be the String value of O’s [[TypedArrayName]] internal slot. + // 15. Let elementSize be the Number value of the Element Size value specified in Table 49 for constructorName. + // 16. Let srcByteOffset be the value of O’s [[ByteOffset]] internal slot. + // 17. Let beginByteOffset be srcByteOffset + beginIndex × elementSize. + JSHandle constructorName = TypedArrayHelper::GetTypedArrayName(thread, thisObj); + int32_t elementSize = TypedArrayHelper::GetSizeFromName(thread, constructorName); + int32_t srcByteOffset = TypedArrayHelper::GetByteOffset(thread, thisObj); + int32_t beginByteOffset = srcByteOffset + beginIndex * elementSize; + // 21. Let argumentsList be «buffer, beginByteOffset, newLength». + JSHandle argumentsList = + thread->GetEcmaVM()->GetFactory()->NewTaggedArray(3); // 3: «buffer, beginByteOffset, newLength» + // 5. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = TypedArrayHelper::GetViewedArrayBuffer(thisObj); + argumentsList->Set(thread, 0, buffer); + argumentsList->Set(thread, 1, JSTaggedValue(beginByteOffset)); + argumentsList->Set(thread, 2, JSTaggedValue(newLength)); // 2: the third arg is newLength + // 22. Return Construct(constructor, argumentsList). + JSHandle newArr = TypedArrayHelper::TypedArraySpeciesCreate(thread, thisObj, argumentsList); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return newArr.GetTaggedValue(); +} + +// 22.2.3.27 +JSTaggedValue BuiltinsTypedArray::ToLocaleString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::ToLocaleString(argv); +} + +// 22.2.3.28 +JSTaggedValue BuiltinsTypedArray::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::ToString(argv); +} + +// 22.2.3.29 +JSTaggedValue BuiltinsTypedArray::Values(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Values); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Let valid be ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // 3. ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + JSHandle self(thisHandle); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 4. Return CreateArrayIterator(O, "value"). + JSHandle iter(factory->NewJSArrayIterator(self, IterationKind::VALUE)); + return iter.GetTaggedValue(); +} + +// 22.2.3.31 +JSTaggedValue BuiltinsTypedArray::ToStringTag(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, ToStringTag); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, return undefined. + if (!thisHandle->IsECMAObject()) { + return JSTaggedValue::Undefined(); + } + // 3. If O does not have a [[TypedArrayName]] internal slot, return undefined. + if (!thisHandle->IsTypedArray()) { + return JSTaggedValue::Undefined(); + } + // 4. Let name be the value of O’s [[TypedArrayName]] internal slot. + JSHandle name = TypedArrayHelper::GetTypedArrayName(thread, JSHandle(thisHandle)); + // 5. Assert: name is a String value. + ASSERT(name->IsString()); + // 6. Return name. + return name.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_typedarray.h b/ecmascript/builtins/builtins_typedarray.h new file mode 100644 index 0000000000000000000000000000000000000000..1f3126199325b3fdc3154377ac2edd6f9b30ddfd --- /dev/null +++ b/ecmascript/builtins/builtins_typedarray.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_TYPEDARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_TYPEDARRAY_H + +#include "ecmascript/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsTypedArray : public base::BuiltinsBase { +public: + // 22.2.1 + static JSTaggedValue TypedArrayBaseConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Int8ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Uint8ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Uint8ClampedArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Int16ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Uint16ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Int32ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Uint32ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Float32ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Float64ArrayConstructor(EcmaRuntimeCallInfo *argv); + + // 22.2.1.2.1 + static JSTaggedValue AllocateTypedArray(EcmaRuntimeCallInfo *argv); + + // 22.2.2.1 + static JSTaggedValue From(EcmaRuntimeCallInfo *argv); + // 22.2.2.2 + static JSTaggedValue Of(EcmaRuntimeCallInfo *argv); + // 22.2.2.4 + static JSTaggedValue Species(EcmaRuntimeCallInfo *argv); + + // prototype + // 22.2.3.1 + static JSTaggedValue GetBuffer(EcmaRuntimeCallInfo *argv); + // 22.2.3.2 + static JSTaggedValue GetByteLength(EcmaRuntimeCallInfo *argv); + // 22.2.3.3 + static JSTaggedValue GetByteOffset(EcmaRuntimeCallInfo *argv); + // 22.2.3.5 + static JSTaggedValue CopyWithin(EcmaRuntimeCallInfo *argv); + // 22.2.3.6 + static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); + // 22.2.3.7 + static JSTaggedValue Every(EcmaRuntimeCallInfo *argv); + // 22.2.3.8 + static JSTaggedValue Fill(EcmaRuntimeCallInfo *argv); + // 22.2.3.9 + static JSTaggedValue Filter(EcmaRuntimeCallInfo *argv); + // 22.2.3.10 + static JSTaggedValue Find(EcmaRuntimeCallInfo *argv); + // 22.2.3.11 + static JSTaggedValue FindIndex(EcmaRuntimeCallInfo *argv); + // 22.2.3.12 + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + // 22.2.3.13 + static JSTaggedValue IndexOf(EcmaRuntimeCallInfo *argv); + // 22.2.3.14 + static JSTaggedValue Join(EcmaRuntimeCallInfo *argv); + // 22.2.3.15 + static JSTaggedValue Keys(EcmaRuntimeCallInfo *argv); + // 22.2.3.16 + static JSTaggedValue LastIndexOf(EcmaRuntimeCallInfo *argv); + // 22.2.3.17 + static JSTaggedValue GetLength(EcmaRuntimeCallInfo *argv); + // 22.2.3.18 + static JSTaggedValue Map(EcmaRuntimeCallInfo *argv); + // 22.2.3.19 + static JSTaggedValue Reduce(EcmaRuntimeCallInfo *argv); + // 22.2.3.20 + static JSTaggedValue ReduceRight(EcmaRuntimeCallInfo *argv); + // 22.2.3.21 + static JSTaggedValue Reverse(EcmaRuntimeCallInfo *argv); + // 22.2.3.22 + static JSTaggedValue Set(EcmaRuntimeCallInfo *argv); + // 22.2.3.23 + static JSTaggedValue Slice(EcmaRuntimeCallInfo *argv); + // 22.2.3.24 + static JSTaggedValue Some(EcmaRuntimeCallInfo *argv); + // 22.2.3.25 + static JSTaggedValue Sort(EcmaRuntimeCallInfo *argv); + // 22.2.3.26 + static JSTaggedValue Subarray(EcmaRuntimeCallInfo *argv); + // 22.2.3.27 + static JSTaggedValue ToLocaleString(EcmaRuntimeCallInfo *argv); + // 22.2.3.28 + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 22.2.3.29 + static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); + // 22.2.3.31 + static JSTaggedValue ToStringTag(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins + +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_TYPEDARRAY_H diff --git a/ecmascript/builtins/builtins_weak_map.cpp b/ecmascript/builtins/builtins_weak_map.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f7c7e46ea31d860207fb915706fd47a2c11fa13d --- /dev/null +++ b/ecmascript/builtins/builtins_weak_map.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_weak_map.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_map_iterator.h" +#include "ecmascript/js_weak_container.h" +#include "ecmascript/linked_hash_table.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsWeakMap::WeakMapConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1.If NewTarget is undefined, throw a TypeError exception + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + // throw type error + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + // 2.Let WeakMap be OrdinaryCreateFromConstructor(NewTarget, "%WeakMapPrototype%", «‍[[WeakMapData]]» ). + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + // 3.returnIfAbrupt() + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle weakMap = JSHandle::Cast(obj); + + // 4.Set weakmap’s [[WeakMapData]] internal slot to a new empty List. + JSTaggedValue linkedMap = LinkedHashMap::Create(thread); + weakMap->SetLinkedMap(thread, linkedMap); + // add data into set from iterable + // 5.If iterable is not present, let iterable be undefined. + // 6.If iterable is either undefined or null, let iter be undefined. + JSHandle iterable = GetCallArg(argv, 0); + // 8.If iter is undefined, return set + if (iterable->IsUndefined() || iterable->IsNull()) { + return weakMap.GetTaggedValue(); + } + if (!iterable->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "iterable is not object", JSTaggedValue::Exception()); + } + // Let adder be Get(weakMap, "set"). + JSHandle adderKey(factory->NewFromString("set")); + JSHandle adder = + JSObject::GetProperty(thread, JSHandle(weakMap), adderKey).GetValue(); + // ReturnIfAbrupt(adder). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue()); + // If IsCallable(adder) is false, throw a TypeError exception + if (!adder->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue()); + } + // Let iter be GetIterator(iterable). + JSHandle iter(JSIterator::GetIterator(thread, iterable)); + // ReturnIfAbrupt(iter). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue()); + JSHandle keyIndex(thread, JSTaggedValue(0)); + JSHandle valueIndex(thread, JSTaggedValue(1)); + JSHandle next = JSIterator::IteratorStep(thread, iter); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + JSMutableHandle status(thread, JSTaggedValue::Undefined()); + while (!next->IsFalse()) { + // ReturnIfAbrupt(next). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + // Let nextValue be IteratorValue(next). + JSHandle nextValue(JSIterator::IteratorValue(thread, next)); + // ReturnIfAbrupt(nextValue). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + JSHandle array(factory->NewTaggedArray(2)); // 2: key and value pair + // If Type(nextItem) is not Object + if (!nextValue->IsECMAObject()) { + JSHandle typeError = factory->GetJSError(ErrorType::TYPE_ERROR, "nextItem is not Object"); + JSHandle record( + factory->NewCompletionRecord(CompletionRecord::THROW, JSHandle(typeError))); + JSTaggedValue ret = JSIterator::IteratorClose(thread, iter, record).GetTaggedValue(); + if (!thread->HasPendingException()) { + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, typeError.GetTaggedValue(), ret); + }; + return ret; + } + // Let k be Get(nextItem, "0"). + JSHandle key = JSObject::GetProperty(thread, nextValue, keyIndex).GetValue(); + // If k is an abrupt completion, return IteratorClose(iter, k). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, key); + } + array->Set(thread, 0, key); + // Let v be Get(nextItem, "1"). + JSHandle value = JSObject::GetProperty(thread, nextValue, valueIndex).GetValue(); + // If v is an abrupt completion, return IteratorClose(iter, v). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, value); + } + + array->Set(thread, 1, value); + // Let status be Call(adder, weakMap, «nextValue.[[value]]»). + JSTaggedValue ret = JSFunction::Call(thread, adder, JSHandle(weakMap), array); + + status.Update(ret); + // If status is an abrupt completion, return IteratorClose(iter, status). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, status); + } + // Let next be IteratorStep(iter). + next = JSIterator::IteratorStep(thread, iter); + } + return weakMap.GetTaggedValue(); +} + +JSTaggedValue BuiltinsWeakMap::Delete(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Delete); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[WeakMapData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception()); + } + + JSHandle weakMap(self); + JSHandle key = GetCallArg(argv, 0); + // 5.if Type(key) is not Object, return false. + if (!key->IsHeapObject()) { + return GetTaggedBoolean(false); + } + return GetTaggedBoolean(JSWeakMap::Delete(thread, weakMap, key)); +} + +JSTaggedValue BuiltinsWeakMap::Has(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Has); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self(GetThis(argv)); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[WeakMapData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception()); + } + JSWeakMap *jsWeakMap = JSWeakMap::Cast(*JSTaggedValue::ToObject(thread, self)); + JSHandle key = GetCallArg(argv, 0); + // 5.if Type(key) is not Object, return false. + if (!key->IsHeapObject()) { + return GetTaggedBoolean(false); + } + return GetTaggedBoolean(jsWeakMap->Has(key.GetTaggedValue())); +} + +JSTaggedValue BuiltinsWeakMap::Get(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Get); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self(GetThis(argv)); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[WeakMapData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception()); + } + JSWeakMap *jsWeakMap = JSWeakMap::Cast(*JSTaggedValue::ToObject(thread, self)); + JSHandle key = GetCallArg(argv, 0); + if (!key->IsHeapObject()) { + return JSTaggedValue::Undefined(); + } + return jsWeakMap->Get(key.GetTaggedValue()); +} + +JSTaggedValue BuiltinsWeakMap::Set(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Set); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[WeakMapData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception()); + } + + JSHandle key = GetCallArg(argv, 0); + if (!key->IsHeapObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not an object.", JSTaggedValue::Exception()); + } + if (key->IsSymbol() || key->IsString()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "key is Symblol or String", JSTaggedValue::Exception()); + } + + JSHandle value = GetCallArg(argv, 1); + + JSHandle map(self); + JSWeakMap::Set(thread, map, key, value); + return map.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_weak_map.h b/ecmascript/builtins/builtins_weak_map.h new file mode 100644 index 0000000000000000000000000000000000000000..89e9031d84ba4819ed7574a5387c7bb2ab14219c --- /dev/null +++ b/ecmascript/builtins/builtins_weak_map.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_WEAK_MAP_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_WEAK_MAP_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsWeakMap : public base::BuiltinsBase { +public: + // 23.3.3.1 + static JSTaggedValue WeakMapConstructor(EcmaRuntimeCallInfo *argv); + // 23.3.3.2 + static JSTaggedValue Delete(EcmaRuntimeCallInfo *argv); + // 23.3.3.3 + static JSTaggedValue Get(EcmaRuntimeCallInfo *argv); + // 23.3.3.4 + static JSTaggedValue Has(EcmaRuntimeCallInfo *argv); + // 23.1.3.5 + static JSTaggedValue Set(EcmaRuntimeCallInfo *argv); + // 23.1.3.6 @@toStringTag +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_MAP_H diff --git a/ecmascript/builtins/builtins_weak_set.cpp b/ecmascript/builtins/builtins_weak_set.cpp new file mode 100644 index 0000000000000000000000000000000000000000..90fb697b2abe52f49182adff6a95307a63ca481c --- /dev/null +++ b/ecmascript/builtins/builtins_weak_set.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "builtins_weak_set.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/js_weak_container.h" +#include "ecmascript/linked_hash_table-inl.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsWeakSet::WeakSetConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakSet, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1.If NewTarget is undefined, throw a TypeError exception + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + // throw type error + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + // 2.Let weakset be OrdinaryCreateFromConstructor(NewTarget, "%WeakSetPrototype%", «‍[[WeakSetData]]» ). + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + // 3.returnIfAbrupt() + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle weakSet = JSHandle::Cast(obj); + // 3.ReturnIfAbrupt(weakSet). + // 4.WeakSet set’s [[WeakSetData]] internal slot to a new empty List. + JSTaggedValue linkedSet = LinkedHashSet::Create(thread); + weakSet->SetLinkedSet(thread, linkedSet); + + // add data into weakset from iterable + // 5.If iterable is not present, let iterable be undefined. + // 6.If iterable is either undefined or null, let iter be undefined. + JSHandle iterable(GetCallArg(argv, 0)); + // 8.If iter is undefined, return weakset + if (iterable->IsUndefined() || iterable->IsNull()) { + return weakSet.GetTaggedValue(); + } + // Let adder be Get(weakset, "add"). + JSHandle adderKey(factory->NewFromString("add")); + JSHandle weakSetHandle(weakSet); + JSHandle adder = JSObject::GetProperty(thread, weakSetHandle, adderKey).GetValue(); + // ReturnIfAbrupt(adder). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue()); + // If IsCallable(adder) is false, throw a TypeError exception + if (!adder->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue()); + } + // Let iter be GetIterator(iterable). + JSHandle iter(JSIterator::GetIterator(thread, iterable)); + + // ReturnIfAbrupt(iter). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue()); + // values in iterator_result may be a JSArray, values[0] = key values[1]=value, used valueIndex to get value from + // jsarray + JSHandle valueIndex(thread, JSTaggedValue(1)); + JSHandle next = JSIterator::IteratorStep(thread, iter); + JSMutableHandle status(thread, JSTaggedValue::Undefined()); + while (!next->IsFalse()) { + // ReturnIfAbrupt(next). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + // Let nextValue be IteratorValue(next). + JSHandle nextValue(JSIterator::IteratorValue(thread, next)); + // ReturnIfAbrupt(nextValue). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, nextValue.GetTaggedValue()); + JSHandle array(factory->NewTaggedArray(1)); + array->Set(thread, 0, nextValue); + if (nextValue->IsArray(thread)) { + auto prop = JSObject::GetProperty(thread, nextValue, valueIndex).GetValue(); + array->Set(thread, 0, prop); + } + JSTaggedValue ret = JSFunction::Call(thread, adder, JSHandle(weakSet), array); + // Let status be Call(adder, weakset, «nextValue.[[value]]»). + status.Update(ret); + // If status is an abrupt completion, return IteratorClose(iter, status). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, status); + } + // Let next be IteratorStep(iter). + next = JSIterator::IteratorStep(thread, iter); + } + return weakSet.GetTaggedValue(); +} + +JSTaggedValue BuiltinsWeakSet::Add(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakSet, Add); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[WeakSetData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakSet", JSTaggedValue::Exception()); + } + + JSHandle value(GetCallArg(argv, 0)); + if (!value->IsHeapObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "value is not an object", JSTaggedValue::Exception()); + } + if (value->IsSymbol() || value->IsString()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "value is Symblol or String", JSTaggedValue::Exception()); + } + + JSHandle weakSet(thread, JSWeakSet::Cast(*JSTaggedValue::ToObject(thread, self))); + + JSWeakSet::Add(thread, weakSet, value); + return weakSet.GetTaggedValue(); +} + +JSTaggedValue BuiltinsWeakSet::Delete(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakSet, Delete); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[WeakSetData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakSet", JSTaggedValue::Exception()); + } + + JSHandle weakSet(thread, JSWeakSet::Cast(*JSTaggedValue::ToObject(thread, self))); + JSHandle value = GetCallArg(argv, 0); + if (!value->IsHeapObject()) { + GetTaggedBoolean(false); + } + return GetTaggedBoolean(JSWeakSet::Delete(thread, weakSet, value)); +} + +JSTaggedValue BuiltinsWeakSet::Has(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakSet, Has); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakSet", JSTaggedValue::Exception()); + } + JSWeakSet *jsWeakSet = JSWeakSet::Cast(*JSTaggedValue::ToObject(thread, self)); + JSHandle value = GetCallArg(argv, 0); + if (!value->IsHeapObject()) { + GetTaggedBoolean(false); + } + return GetTaggedBoolean(jsWeakSet->Has(value.GetTaggedValue())); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_weak_set.h b/ecmascript/builtins/builtins_weak_set.h new file mode 100644 index 0000000000000000000000000000000000000000..bc8a8166ecfd0d67ec4d4299b42199ac403e2c78 --- /dev/null +++ b/ecmascript/builtins/builtins_weak_set.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BUILTINS_WEAK_SET_H +#define PANDA_RUNTIME_ECMASCRIPT_BUILTINS_WEAK_SET_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsWeakSet : public base::BuiltinsBase { +public: + // 23.4.1.1 + static JSTaggedValue WeakSetConstructor(EcmaRuntimeCallInfo *argv); + // 23.4.3.1 + static JSTaggedValue Add(EcmaRuntimeCallInfo *argv); + // 23.4.3.3 + static JSTaggedValue Delete(EcmaRuntimeCallInfo *argv); + // 23.4.3.4 + static JSTaggedValue Has(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_SET_H diff --git a/ecmascript/builtins/tests/BUILD.gn b/ecmascript/builtins/tests/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..bc966f49bdc73fac67e8b5cd5d8231443e9a66ce --- /dev/null +++ b/ecmascript/builtins/tests/BUILD.gn @@ -0,0 +1,585 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//ark/js_runtime/test/test_helper.gni") +import("//build/test.gni") + +module_output_path = "ark/js_runtime" + +host_unittest_action("BuiltinsArraybufferTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_arraybuffer_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsArrayTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_array_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsBooleanTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_boolean_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsDataviewTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_dataview_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsDateTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_date_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsErrorsTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_errors_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsFunctionTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_function_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsIteratorTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_iterator_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsJsonTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_json_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsMapTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_map_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsMathTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_math_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsNumberTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_number_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsObjectTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_object_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsPromiseTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_promise_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsProxyTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_proxy_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsReflectTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_reflect_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsRegexpTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_regexp_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsSetTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_set_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsStringTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_string_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsSymbolTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_symbol_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsTypedarrayTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_typedarray_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsWeakMapTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_weak_map_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsWeakSetTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_weak_set_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +group("unittest") { + testonly = true + + # deps file + deps = [ + ":BuiltinsArrayTest", + ":BuiltinsArraybufferTest", + ":BuiltinsBooleanTest", + ":BuiltinsDataviewTest", + ":BuiltinsDateTest", + ":BuiltinsErrorsTest", + ":BuiltinsFunctionTest", + ":BuiltinsIteratorTest", + ":BuiltinsJsonTest", + ":BuiltinsMapTest", + ":BuiltinsMathTest", + ":BuiltinsNumberTest", + ":BuiltinsObjectTest", + ":BuiltinsPromiseTest", + ":BuiltinsProxyTest", + ":BuiltinsReflectTest", + ":BuiltinsRegexpTest", + ":BuiltinsSetTest", + ":BuiltinsStringTest", + ":BuiltinsSymbolTest", + ":BuiltinsTypedarrayTest", + ":BuiltinsWeakMapTest", + ":BuiltinsWeakSetTest", + ] +} + +group("host_unittest") { + testonly = true + + # deps file + deps = [ + ":BuiltinsArrayTestAction(${host_toolchain})", + ":BuiltinsArraybufferTestAction(${host_toolchain})", + ":BuiltinsBooleanTestAction(${host_toolchain})", + ":BuiltinsDataviewTestAction(${host_toolchain})", + ":BuiltinsDateTestAction(${host_toolchain})", + ":BuiltinsErrorsTestAction(${host_toolchain})", + ":BuiltinsFunctionTestAction(${host_toolchain})", + ":BuiltinsIteratorTestAction(${host_toolchain})", + ":BuiltinsJsonTestAction(${host_toolchain})", + ":BuiltinsMapTestAction(${host_toolchain})", + ":BuiltinsMathTestAction(${host_toolchain})", + ":BuiltinsNumberTestAction(${host_toolchain})", + ":BuiltinsObjectTestAction(${host_toolchain})", + ":BuiltinsPromiseTestAction(${host_toolchain})", + ":BuiltinsProxyTestAction(${host_toolchain})", + ":BuiltinsReflectTestAction(${host_toolchain})", + ":BuiltinsRegexpTestAction(${host_toolchain})", + ":BuiltinsSetTestAction(${host_toolchain})", + ":BuiltinsStringTestAction(${host_toolchain})", + ":BuiltinsSymbolTestAction(${host_toolchain})", + ":BuiltinsTypedarrayTestAction(${host_toolchain})", + ":BuiltinsWeakMapTestAction(${host_toolchain})", + ":BuiltinsWeakSetTestAction(${host_toolchain})", + ] +} diff --git a/ecmascript/builtins/tests/builtins_array_test.cpp b/ecmascript/builtins/tests/builtins_array_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64967fe0a8a16e0234eba5dc22b556d4687792d0 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_array_test.cpp @@ -0,0 +1,1303 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_array.h" + +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_array_iterator.h" + +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" + +#include "ecmascript/object_factory.h" +#include "ecmascript/object_operator.h" +#include "ecmascript/tests/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +using namespace panda::ecmascript::base; + +namespace panda::test { +using Array = ecmascript::builtins::BuiltinsArray; +class BuiltinsArrayTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv) + { + JSHandle key = GetCallArg(argv, 0); + if (key->IsUndefined()) { + return JSTaggedValue::Undefined(); + } + JSArray *jsArray = JSArray::Cast(GetThis(argv)->GetTaggedObject()); + int length = jsArray->GetArrayLength() + 1; + jsArray->SetArrayLength(argv->GetThread(), length); + return JSTaggedValue::Undefined(); + } + + static JSTaggedValue TestEveryFunc(EcmaRuntimeCallInfo *argv) + { + array_size_t argc = argv->GetArgsNumber(); + if (argc > 0) { + if (GetCallArg(argv, 0)->GetInt() > 10) { // 10 : test case + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestMapFunc(EcmaRuntimeCallInfo *argv) + { + int accumulator = GetCallArg(argv, 0)->GetInt(); + accumulator = accumulator * 2; // 2 : mapped to 2 times the original value + return BuiltinsBase::GetTaggedInt(accumulator); + } + + static JSTaggedValue TestFindFunc(EcmaRuntimeCallInfo *argv) + { + array_size_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestFindIndexFunc(EcmaRuntimeCallInfo *argv) + { + array_size_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestReduceFunc(EcmaRuntimeCallInfo *argv) + { + int accumulator = GetCallArg(argv, 0)->GetInt(); + accumulator = accumulator + GetCallArg(argv, 1)->GetInt(); + return BuiltinsBase::GetTaggedInt(accumulator); + } + + static JSTaggedValue TestReduceRightFunc(EcmaRuntimeCallInfo *argv) + { + int accumulator = GetCallArg(argv, 0)->GetInt(); + accumulator = accumulator + GetCallArg(argv, 1)->GetInt(); + return BuiltinsBase::GetTaggedInt(accumulator); + } + + static JSTaggedValue TestSomeFunc(EcmaRuntimeCallInfo *argv) + { + array_size_t argc = argv->GetArgsNumber(); + if (argc > 0) { + if (GetCallArg(argv, 0)->GetInt() > 10) { // 10 : test case + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + }; +}; + +JSTaggedValue CreateBuiltinsJSObject(JSThread *thread, const CString keyCStr) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + + JSHandle dynclass = globalEnv->GetObjectFunction(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + JSHandle key(factory->NewFromString(&keyCStr[0])); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, obj, key, value); + return obj.GetTaggedValue(); +} + +HWTEST_F_L0(BuiltinsArrayTest, ArrayConstructor) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle array(env->GetArrayFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo1->SetFunction(array.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::ArrayConstructor(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + JSHandle key0(thread, JSTaggedValue(0)); + JSHandle key1(thread, JSTaggedValue(1)); + JSHandle key2(thread, JSTaggedValue(2)); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5)); +} + +// 22.1.2.1 Array.from ( items [ , mapfn [ , thisArg ] ] ) +HWTEST_F_L0(BuiltinsArrayTest, From) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0))->GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::From(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(2)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, valueHandle, key3, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4)); + JSObject::GetOwnProperty(thread, valueHandle, key4, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5)); +} + +// 22.1.2.2 Array.isArray(arg) +HWTEST_F_L0(BuiltinsArrayTest, IsArray) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::IsArray(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast(1))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result = Array::IsArray(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +HWTEST_F_L0(BuiltinsArrayTest, Of) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle array(env->GetArrayFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo1->SetFunction(array.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Of(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + JSHandle key0(thread, JSTaggedValue(0)); + JSHandle key1(thread, JSTaggedValue(1)); + JSHandle key2(thread, JSTaggedValue(2)); + + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5)); +} + +HWTEST_F_L0(BuiltinsArrayTest, Species) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle array(env->GetArrayFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(array.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(globalObject.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Species(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result.IsECMAObject()); +} + +HWTEST_F_L0(BuiltinsArrayTest, Concat) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSArray *arr1 = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + EXPECT_TRUE(arr1 != nullptr); + JSHandle obj1(thread, arr1); + JSHandle key4(thread, JSTaggedValue(0)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj1, key4, desc4); + JSHandle key5(thread, JSTaggedValue(1)); + PropertyDescriptor desc5(thread, JSHandle(thread, JSTaggedValue(5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj1, key5, desc5); + JSHandle key6(thread, JSTaggedValue(2)); + PropertyDescriptor desc6(thread, JSHandle(thread, JSTaggedValue(6)), true, true, true); + JSArray::DefineOwnProperty(thread, obj1, key6, desc6); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, obj1.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Concat(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + JSHandle key7(thread, JSTaggedValue(5)); + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(valueHandle), lengthKeyHandle).GetValue()->GetInt(), 6); + JSObject::GetOwnProperty(thread, valueHandle, key7, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(6)); +} + +// 22.1.3.3 new Array(1,2,3,4,5).CopyWithin(0,3,5) +HWTEST_F_L0(BuiltinsArrayTest, CopyWithin) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::CopyWithin(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, valueHandle, key3, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4)); + JSObject::GetOwnProperty(thread, valueHandle, key4, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5)); +} + +HWTEST_F_L0(BuiltinsArrayTest, Every) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(100)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(200)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(300)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle jsArray = factory->NewJSArray(); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestEveryFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = Array::Every(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +HWTEST_F_L0(BuiltinsArrayTest, Map) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(50)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(200)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestMapFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Map(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(valueHandle), lengthKeyHandle).GetValue()->GetInt(), 3); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + + ASSERT_EQ(descRes.GetValue()->GetInt(), 100); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue()->GetInt(), 400); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue()->GetInt(), 6); +} + +HWTEST_F_L0(BuiltinsArrayTest, Reverse) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(50)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(200)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Reverse(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(valueHandle), lengthKeyHandle).GetValue()->GetInt(), 3); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(200)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(50)); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 3); + JSObject::GetOwnProperty(thread, obj, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, obj, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(200)); + JSObject::GetOwnProperty(thread, obj, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(50)); +} + +HWTEST_F_L0(BuiltinsArrayTest, Slice) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(4))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Slice(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(valueHandle), lengthKeyHandle).GetValue()->GetInt(), 3); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(2)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4)); +} + +HWTEST_F_L0(BuiltinsArrayTest, Splice) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(2))); + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(100))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Splice(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 4); + + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(valueHandle), lengthKeyHandle).GetValue()->GetInt(), 2); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(2)); +} + +// 22.1.3.6 new Array(1,2,3,4,5).Fill(0,1,3) +HWTEST_F_L0(BuiltinsArrayTest, Fill) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Fill(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(0)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(0)); + JSObject::GetOwnProperty(thread, valueHandle, key3, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4)); + JSObject::GetOwnProperty(thread, valueHandle, key4, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5)); +} + +HWTEST_F_L0(BuiltinsArrayTest, Find) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(102)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestFindFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = Array::Find(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue(102).GetRawData()); +} + +HWTEST_F_L0(BuiltinsArrayTest, FindIndex) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(30)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestFindIndexFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = Array::FindIndex(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsArrayTest, ForEach) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForEachFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = Array::ForEach(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + EXPECT_EQ(jsArray->GetArrayLength(), 3); +} + +// 22.1.3.11 new Array(1,2,3,4,3).IndexOf(searchElement [ , fromIndex ]) +HWTEST_F_L0(BuiltinsArrayTest, IndexOf) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::IndexOf(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue(static_cast(3))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result = Array::IndexOf(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(4)).GetRawData()); + + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo3->SetCallArg(1, JSTaggedValue(static_cast(0))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + result = Array::IndexOf(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); + + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(static_cast(3))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + result = Array::IndexOf(ecmaRuntimeCallInfo4.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); +} + +// 22.1.3.14 new Array(1,2,3,4,3).LastIndexOf(searchElement [ , fromIndex ]) +HWTEST_F_L0(BuiltinsArrayTest, LastIndexOf) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + // new Array(1,2,3,4,3).LastIndexOf(3,4) + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(4))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::LastIndexOf(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(4)).GetRawData()); + + // new Array(1,2,3,4,3).LastIndexOf(3,3) + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue(static_cast(3))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result = Array::LastIndexOf(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); + + // new Array(1,2,3,4,3).LastIndexOf(5,4) + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo3->SetCallArg(1, JSTaggedValue(static_cast(4))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + result = Array::LastIndexOf(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); + + // new Array(1,2,3,4,3).LastIndexOf(3) + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(static_cast(3))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + result = Array::LastIndexOf(ecmaRuntimeCallInfo4.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(4)).GetRawData()); +} + +// 22.1.3.11 new Array().Pop() +HWTEST_F_L0(BuiltinsArrayTest, Pop) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Pop(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result = Array::Pop(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(3).GetRawData()); +} + +// 22.1.3.11 new Array(1,2,3).Push(...items) +HWTEST_F_L0(BuiltinsArrayTest, Push) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(4))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Push(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetNumber(), 5); + + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 5); + JSHandle key3(thread, JSTaggedValue(3)); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), key3).GetValue()->GetInt(), 4); + JSHandle key4(thread, JSTaggedValue(4)); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), key4).GetValue()->GetInt(), 5); +} + +HWTEST_F_L0(BuiltinsArrayTest, Reduce) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestReduceFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Reduce(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(16).GetRawData()); +} + +HWTEST_F_L0(BuiltinsArrayTest, ReduceRight) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestReduceRightFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::ReduceRight(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(16).GetRawData()); +} + +HWTEST_F_L0(BuiltinsArrayTest, Shift) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Shift(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(1).GetRawData()); +} + +HWTEST_F_L0(BuiltinsArrayTest, Some) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(20)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestSomeFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = Array::Some(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +HWTEST_F_L0(BuiltinsArrayTest, Sort) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = Array::Sort(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_TRUE(result2.IsECMAObject()); + JSHandle resultArr = + JSHandle(thread, JSTaggedValue(static_cast(result2.GetRawData()))); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key0).GetValue()->GetInt(), 1); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key1).GetValue()->GetInt(), 2); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key2).GetValue()->GetInt(), 3); +} + +HWTEST_F_L0(BuiltinsArrayTest, Unshift) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(4))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Unshift(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(5)).GetRawData()); + + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 5); + JSHandle key3(thread, JSTaggedValue(0)); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), key3).GetValue()->GetInt(), 4); + JSHandle key4(thread, JSTaggedValue(1)); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), key4).GetValue()->GetInt(), 5); +} + +HWTEST_F_L0(BuiltinsArrayTest, Join) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, obj, lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(2))); + JSArray::DefineOwnProperty(thread, JSHandle(obj), key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(3))); + JSArray::DefineOwnProperty(thread, JSHandle(obj), key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(4))); + JSArray::DefineOwnProperty(thread, JSHandle(obj), key2, desc2); + + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromString("2,3,4"); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Join(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + [[maybe_unused]] auto *res = EcmaString::Cast(resultHandle.GetTaggedValue().GetTaggedObject()); + + ASSERT_EQ(res->Compare(*str), 0); +} + +HWTEST_F_L0(BuiltinsArrayTest, ToString) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, obj, lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(2))); + JSArray::DefineOwnProperty(thread, JSHandle(obj), key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(3))); + JSArray::DefineOwnProperty(thread, JSHandle(obj), key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(4))); + JSArray::DefineOwnProperty(thread, JSHandle(obj), key2, desc2); + + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromString("2,3,4"); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Join(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + [[maybe_unused]] auto *res = EcmaString::Cast(resultHandle.GetTaggedValue().GetTaggedObject()); + + ASSERT_EQ(res->Compare(*str), 0); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_arraybuffer_test.cpp b/ecmascript/builtins/tests/builtins_arraybuffer_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e76d8400776844cc38e7ae1f3e6f0bdc50025da9 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_arraybuffer_test.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_arraybuffer.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" + +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsArrayBufferTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSTaggedValue CreateBuiltinsArrayBuffer(JSThread *thread, int32_t length) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrayBuffer(thread, env->GetArrayBufferFunction().GetTaggedValue()); + JSHandle globalObject(thread, env->GetGlobalObject()); + // 6 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, arrayBuffer.GetTaggedValue(), 6); + ecmaRuntimeCallInfo->SetFunction(arrayBuffer.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(length)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsArrayBuffer::ArrayBufferConstructor(ecmaRuntimeCallInfo.get()); + return result; +} + +// new ArrayBuffer(8) +HWTEST_F_L0(BuiltinsArrayBufferTest, Constructor1) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrayBuffer(thread, env->GetArrayBufferFunction().GetTaggedValue()); + JSHandle globalObject(thread, env->GetGlobalObject()); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, arrayBuffer.GetTaggedValue(), 6); + ecmaRuntimeCallInfo->SetFunction(arrayBuffer.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(8))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsArrayBuffer::ArrayBufferConstructor(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); +} + +// (new ArrayBuffer(5)).byteLength +HWTEST_F_L0(BuiltinsArrayBufferTest, byteLength1) +{ + JSTaggedValue tagged = CreateBuiltinsArrayBuffer(thread, 5); + JSHandle arrBuf(thread, JSArrayBuffer::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(arrBuf.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsArrayBuffer::GetByteLength(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(5).GetRawData()); +} + +// (new ArrayBuffer(10)).slice(1, 5).bytelength +HWTEST_F_L0(BuiltinsArrayBufferTest, slice1) +{ + JSTaggedValue tagged = CreateBuiltinsArrayBuffer(thread, 10); + JSHandle arrBuf(thread, JSArrayBuffer::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(arrBuf.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsArrayBuffer::Slice(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + JSHandle arrBuf1(thread, + JSArrayBuffer::Cast(reinterpret_cast(result1.GetRawData()))); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(arrBuf1.GetTaggedValue()); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsArrayBuffer::GetByteLength(ecmaRuntimeCallInfo1.get()); + + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(4).GetRawData()); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_boolean_test.cpp b/ecmascript/builtins/tests/builtins_boolean_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5163a403fd6d97166752e3e46faf1ac580ab8d52 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_boolean_test.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_boolean.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +using BuiltinsBase = panda::ecmascript::base::BuiltinsBase; + +namespace panda::test { +class BuiltinsBooleanTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +// new Boolean(123) +HWTEST_F_L0(BuiltinsBooleanTest, BooleanConstructor) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle boolean(env->GetBooleanFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*boolean), 6); + ecmaRuntimeCallInfo->SetFunction(boolean.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(123))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + ASSERT_EQ(JSPrimitiveRef::Cast(result.GetTaggedObject())->GetValue().IsTrue(), 1); +} + +// new Boolean(undefined) +HWTEST_F_L0(BuiltinsBooleanTest, BooleanConstructor1) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle boolean(env->GetBooleanFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*boolean), 6); + ecmaRuntimeCallInfo->SetFunction(boolean.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + ASSERT_EQ(JSPrimitiveRef::Cast(result.GetTaggedObject())->GetValue().IsFalse(), 1); +} + +// Boolean("helloworld") +HWTEST_F_L0(BuiltinsBooleanTest, BooleanConstructor2) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle boolean(env->GetBooleanFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromString("helloworld"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(boolean.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo.get()); + + JSTaggedValue ruler = BuiltinsBase::GetTaggedBoolean(true); + ASSERT_EQ(result.GetRawData(), ruler.GetRawData()); +} + +// false.toString() +HWTEST_F_L0(BuiltinsBooleanTest, BooleanPrototypeToString) +{ + ASSERT_NE(thread, nullptr); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanPrototypeToString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + auto ruler = thread->GetEcmaVM()->GetFactory()->NewFromString("false"); + ASSERT_EQ(res->Compare(*ruler), 0); +} + +// (new Boolean(true)).toString() +HWTEST_F_L0(BuiltinsBooleanTest, BooleanPrototypeToString1) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle booleanObject(env->GetBooleanFunction()); + JSHandle value(thread, JSTaggedValue::True()); + JSHandle boolean = thread->GetEcmaVM()->GetFactory()->NewJSPrimitiveRef(booleanObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(boolean.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanPrototypeToString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + auto ruler = thread->GetEcmaVM()->GetFactory()->NewFromString("true"); + ASSERT_EQ(res->Compare(*ruler), 0); +} + +// true.valueOf() +HWTEST_F_L0(BuiltinsBooleanTest, BooleanPrototypeValueOf) +{ + ASSERT_NE(thread, nullptr); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanPrototypeValueOf(ecmaRuntimeCallInfo.get()); + + JSTaggedValue ruler = BuiltinsBase::GetTaggedBoolean(true); + ASSERT_EQ(result.GetRawData(), ruler.GetRawData()); +} + +// (new Boolean(false)).valueOf() +HWTEST_F_L0(BuiltinsBooleanTest, BooleanPrototypeValueOf1) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle booleanObject(env->GetBooleanFunction()); + JSHandle value(thread, JSTaggedValue::False()); + JSHandle boolean = thread->GetEcmaVM()->GetFactory()->NewJSPrimitiveRef(booleanObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(boolean.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanPrototypeValueOf(ecmaRuntimeCallInfo.get()); + + JSTaggedValue ruler = BuiltinsBase::GetTaggedBoolean(false); + ASSERT_EQ(result.GetRawData(), ruler.GetRawData()); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_dataview_test.cpp b/ecmascript/builtins/tests/builtins_dataview_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..931045a1fb1d9f57f1661f920cb248550ea273e3 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_dataview_test.cpp @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_dataview.h" +#include "ecmascript/builtins/builtins_arraybuffer.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_dataview.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using DataViewType = ecmascript::DataViewType; +class BuiltinsDataViewTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSTaggedValue CreateBuiltinsArrayBuffer(JSThread *thread, int32_t length) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrayBuffer(thread, env->GetArrayBufferFunction().GetTaggedValue()); + JSHandle globalObject(thread, env->GetGlobalObject()); + // 6 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*arrayBuffer), 6); + ecmaRuntimeCallInfo->SetFunction(arrayBuffer.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(length)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsArrayBuffer::ArrayBufferConstructor(ecmaRuntimeCallInfo.get()); + return result; +} + +JSTaggedValue CreateBuiltinsDataView(JSThread *thread, int32_t length, int32_t byte_offset) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle dataView(thread, env->GetDataViewFunction().GetTaggedValue()); + JSHandle globalObject(thread, env->GetGlobalObject()); + JSTaggedValue tagged = CreateBuiltinsArrayBuffer(thread, length); + JSHandle arrBuf(thread, JSArrayBuffer::Cast(reinterpret_cast(tagged.GetRawData()))); + // 8 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*dataView), 8); + ecmaRuntimeCallInfo->SetFunction(dataView.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, arrBuf.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(byte_offset)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::DataViewConstructor(ecmaRuntimeCallInfo.get()); + return result; +} + +void SetUint8(JSThread *thread, const JSHandle &view, int32_t offset, JSTaggedValue value) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(offset)); + ecmaRuntimeCallInfo->SetCallArg(1, value); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDataView::SetUint8(ecmaRuntimeCallInfo.get()); +} + +// new DataView(new ArrayBuffer(10), 1) +HWTEST_F_L0(BuiltinsDataViewTest, Constructor) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle dataView(thread, env->GetDataViewFunction().GetTaggedValue()); + JSHandle globalObject(thread, env->GetGlobalObject()); + JSTaggedValue tagged = CreateBuiltinsArrayBuffer(thread, 10); + JSHandle arrBuf(thread, JSArrayBuffer::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*dataView), 8); + ecmaRuntimeCallInfo->SetFunction(dataView.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, arrBuf.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(1)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::DataViewConstructor(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); +} + +// new DataView(new ArrayBuffer(10), 1).byteOffset +HWTEST_F_L0(BuiltinsDataViewTest, byteOffset) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 10, 1); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetOffset(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(1).GetRawData()); +} + +// new DataView(new ArrayBuffer(10), 2).byteLength +HWTEST_F_L0(BuiltinsDataViewTest, byteLength) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 10, 2); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetByteLength(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(8).GetRawData()); +} + +// new DataView(new ArrayBuffer(10), 1).buffer +HWTEST_F_L0(BuiltinsDataViewTest, buffer) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 10, 1); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetBuffer(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.IsArrayBuffer(), true); +} + +// new DataView(new ArrayBuffer(8), 0).SetUint16/GetUint16 +HWTEST_F_L0(BuiltinsDataViewTest, getUint16) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-1870724872)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetUint16(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue::True()); + + JSTaggedValue result1 = BuiltinsDataView::GetUint16(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(63488).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetInt16/GetInt16 +HWTEST_F_L0(BuiltinsDataViewTest, getInt16) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-1870724872)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetInt16(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue::True()); + + JSTaggedValue result1 = BuiltinsDataView::GetInt16(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(-2048).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetUint8/GetUint32 +HWTEST_F_L0(BuiltinsDataViewTest, GetUint32) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + SetUint8(thread, view, 0, JSTaggedValue(127)); + SetUint8(thread, view, 1, JSTaggedValue(255)); + SetUint8(thread, view, 2, JSTaggedValue(255)); + SetUint8(thread, view, 3, JSTaggedValue(255)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetUint32(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(2147483647).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetUint8/GetInt32 +HWTEST_F_L0(BuiltinsDataViewTest, GetInt32) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + SetUint8(thread, view, 0, JSTaggedValue(127)); + SetUint8(thread, view, 1, JSTaggedValue(255)); + SetUint8(thread, view, 2, JSTaggedValue(255)); + SetUint8(thread, view, 3, JSTaggedValue(255)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetInt32(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(2147483647).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetUint8/GetInt8 +HWTEST_F_L0(BuiltinsDataViewTest, GetInt8) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + SetUint8(thread, view, 0, JSTaggedValue(255)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetInt8(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetUint8/GetUint8 +HWTEST_F_L0(BuiltinsDataViewTest, GetUint8) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + SetUint8(thread, view, 0, JSTaggedValue(127)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetUint8(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(127).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 4).SetUint8/GetFloat32 +HWTEST_F_L0(BuiltinsDataViewTest, GetFloat32) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + SetUint8(thread, view, 4, JSTaggedValue(75)); + SetUint8(thread, view, 5, JSTaggedValue(75)); + SetUint8(thread, view, 6, JSTaggedValue(75)); + SetUint8(thread, view, 7, JSTaggedValue(75)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(4)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetFloat32(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(13323083)).GetRawData()); +} + +// new DataView(new ArrayBuffer(12), 4).SetUint8/GetFloat64 +HWTEST_F_L0(BuiltinsDataViewTest, GetFloat64) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 12, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + SetUint8(thread, view, 4, JSTaggedValue(67)); + SetUint8(thread, view, 5, JSTaggedValue(67)); + SetUint8(thread, view, 6, JSTaggedValue(68)); + SetUint8(thread, view, 7, JSTaggedValue(68)); + SetUint8(thread, view, 8, JSTaggedValue(67)); + SetUint8(thread, view, 9, JSTaggedValue(67)); + SetUint8(thread, view, 10, JSTaggedValue(68)); + SetUint8(thread, view, 11, JSTaggedValue(68)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(4)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetFloat64(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(10846169068898440)).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetUint32/GetUint32 +HWTEST_F_L0(BuiltinsDataViewTest, SetUint32) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(0x907f00f8)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetUint32(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue::False()); + + JSTaggedValue result1 = BuiltinsDataView::GetUint32(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(0xf8007f90)).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetInt32/GetInt32 +HWTEST_F_L0(BuiltinsDataViewTest, SetInt32) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-1870724872)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetInt32(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue::False()); + + JSTaggedValue result1 = BuiltinsDataView::GetInt32(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(-134185072).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetInt8/GetUint8 +HWTEST_F_L0(BuiltinsDataViewTest, SetInt8) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-1)); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetInt8(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + JSTaggedValue result1 = BuiltinsDataView::GetUint8(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(255).GetRawData()); +} + +// new DataView(new ArrayBuffer(4), 0).SetFloat32/GetFloat32 +HWTEST_F_L0(BuiltinsDataViewTest, SetFloat32) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 4, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(42)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetFloat32(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue::False()); + + JSTaggedValue result1 = BuiltinsDataView::GetFloat32(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1.4441781973331565e-41)).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetFloat64/GetFloat64 +HWTEST_F_L0(BuiltinsDataViewTest, SetFloat64) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(42)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetFloat64(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue::False()); + + JSTaggedValue result1 = BuiltinsDataView::GetFloat64(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(8.759e-320)).GetRawData()); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_date_test.cpp b/ecmascript/builtins/tests/builtins_date_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eff692581f85002de8adff766c846dac19ed9235 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_date_test.cpp @@ -0,0 +1,1126 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/string_helper.h" +#include "ecmascript/builtins/builtins_date.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/tests/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +namespace panda::test { +const char NEG = '-'; +const char PLUS = '+'; +const int STR_LENGTH_OTHERS = 2; +const int MINUTE_PER_HOUR = 60; +const int CHINA_BEFORE_1901_MIN = 485; +const int CHINA_AFTER_1901_MIN = 480; +const int64_t CHINA_BEFORE_1900_MS = -2177481943000; +class BuiltinsDateTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSHandle JSDateCreateTest(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVM->GetFactory(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle dateFunction = globalEnv->GetDateFunction(); + JSHandle dateObject = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(dateFunction), dateFunction)); + return dateObject; +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetDate) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + [[maybe_unused]] JSTaggedValue result1 = BuiltinsDate::SetDate(ecmaRuntimeCallInfo.get()); + JSTaggedValue result2 = BuiltinsDate::GetDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetUTCDate) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + [[maybe_unused]] JSTaggedValue result3 = BuiltinsDate::SetUTCDate(ecmaRuntimeCallInfo.get()); + JSTaggedValue result4 = BuiltinsDate::GetUTCDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetMinusDate) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + [[maybe_unused]] JSTaggedValue result3 = BuiltinsDate::SetDate(ecmaRuntimeCallInfo.get()); + JSTaggedValue result4 = BuiltinsDate::GetDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(29)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetMinusUTCDate) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + [[maybe_unused]] JSTaggedValue result3 = BuiltinsDate::SetUTCDate(ecmaRuntimeCallInfo.get()); + JSTaggedValue result4 = BuiltinsDate::GetUTCDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(29)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetFullYear) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + // 2018 : test case + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2018))); + // 10 : test case + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(10))); + // 2, 6 : test case + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(6))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetFullYear(ecmaRuntimeCallInfo.get()); + // 2018 : test case + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(2018)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetMonth(ecmaRuntimeCallInfo.get()); + // 10 : test case + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); + + JSTaggedValue result3 = BuiltinsDate::GetDate(ecmaRuntimeCallInfo.get()); + // 6 : test case + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(6)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetUTCFullYear) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + // 2018 : test case + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2018))); + // 10 : test case + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(10))); + // 2, 6 : test case + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(6))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCFullYear(ecmaRuntimeCallInfo.get()); + JSTaggedValue result4 = BuiltinsDate::GetUTCFullYear(ecmaRuntimeCallInfo.get()); + // 2018 : test case + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(2018)).GetRawData()); + + JSTaggedValue result5 = BuiltinsDate::GetUTCMonth(ecmaRuntimeCallInfo.get()); + // 10 : test case + ASSERT_EQ(result5.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); + + JSTaggedValue result6 = BuiltinsDate::GetUTCDate(ecmaRuntimeCallInfo.get()); + // 6 : test case + ASSERT_EQ(result6.GetRawData(), JSTaggedValue(static_cast(6)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetMinusFullYear) +{ + JSHandle jsDate = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-2018))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-10))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(-6))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetFullYear(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(-2019)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetMonth(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(1)).GetRawData()); + + JSTaggedValue result3 = BuiltinsDate::GetDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(22)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetMinusUTCFullYear) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-2018))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-10))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(-6))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCFullYear(ecmaRuntimeCallInfo.get()); + JSTaggedValue result4 = BuiltinsDate::GetUTCFullYear(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(-2019)).GetRawData()); + + JSTaggedValue result5 = BuiltinsDate::GetUTCMonth(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result5.GetRawData(), JSTaggedValue(static_cast(1)).GetRawData()); + + JSTaggedValue result6 = BuiltinsDate::GetUTCDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result6.GetRawData(), JSTaggedValue(static_cast(22)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetHours) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(18))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(10))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(6))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(static_cast(111))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetHours(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetHours(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(18)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetMinutes(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); + + JSTaggedValue result3 = BuiltinsDate::GetSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(6)).GetRawData()); + + JSTaggedValue result4 = BuiltinsDate::GetMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(111)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetUTCHours) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(18))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(10))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(6))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(static_cast(111))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCHours(ecmaRuntimeCallInfo.get()); + JSTaggedValue result5 = BuiltinsDate::GetUTCHours(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result5.GetRawData(), JSTaggedValue(static_cast(18)).GetRawData()); + + JSTaggedValue result6 = BuiltinsDate::GetUTCMinutes(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result6.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); + + JSTaggedValue result7 = BuiltinsDate::GetUTCSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result7.GetRawData(), JSTaggedValue(static_cast(6)).GetRawData()); + + JSTaggedValue result8 = BuiltinsDate::GetUTCMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result8.GetRawData(), JSTaggedValue(static_cast(111)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetMinusHours) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-18))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-10))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(-6))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(static_cast(-111))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetHours(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetHours(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(5)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetMinutes(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(49)).GetRawData()); + + JSTaggedValue result3 = BuiltinsDate::GetSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(53)).GetRawData()); + + JSTaggedValue result4 = BuiltinsDate::GetMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(889)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetMinusUTCHours) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-18))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-10))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(-6))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(static_cast(-111))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCHours(ecmaRuntimeCallInfo.get()); + JSTaggedValue result5 = BuiltinsDate::GetUTCHours(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result5.GetRawData(), JSTaggedValue(static_cast(5)).GetRawData()); + + JSTaggedValue result6 = BuiltinsDate::GetUTCMinutes(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result6.GetRawData(), JSTaggedValue(static_cast(49)).GetRawData()); + + JSTaggedValue result7 = BuiltinsDate::GetUTCSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result7.GetRawData(), JSTaggedValue(static_cast(53)).GetRawData()); + + JSTaggedValue result8 = BuiltinsDate::GetUTCMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result8.GetRawData(), JSTaggedValue(static_cast(889)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetMilliseconds) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(100))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::SetMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(100)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(100)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetUTCMilliseconds) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(100))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result3 = BuiltinsDate::SetUTCMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(100)).GetRawData()); + + JSTaggedValue result4 = BuiltinsDate::GetUTCMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(100)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetMinutes) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(10))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(6))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(111))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetMinutes(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetMinutes(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(6)).GetRawData()); + + JSTaggedValue result3 = BuiltinsDate::GetMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(111)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetUTCMinutes) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(10))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(6))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(111))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCMinutes(ecmaRuntimeCallInfo.get()); + JSTaggedValue result4 = BuiltinsDate::GetUTCMinutes(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); + + JSTaggedValue result5 = BuiltinsDate::GetUTCSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result5.GetRawData(), JSTaggedValue(static_cast(6)).GetRawData()); + + JSTaggedValue result6 = BuiltinsDate::GetUTCMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result6.GetRawData(), JSTaggedValue(static_cast(111)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetMonth) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(8))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetMonth(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetMonth(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(8)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(3)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetUTCMonth) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(8))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCMonth(ecmaRuntimeCallInfo.get()); + JSTaggedValue result3 = BuiltinsDate::GetUTCMonth(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(8)).GetRawData()); + + JSTaggedValue result4 = BuiltinsDate::GetUTCDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(3)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetSeconds) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(59))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(123))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetSeconds(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(59)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(123)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetUTCSeconds) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(59))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(123))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCSeconds(ecmaRuntimeCallInfo.get()); + JSTaggedValue result3 = BuiltinsDate::GetUTCSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(59)).GetRawData()); + + JSTaggedValue result4 = BuiltinsDate::GetUTCMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(123)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, SetGetTime) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::SetTime(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetTime(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, UTC) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(2020.982)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(10.23)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(4.32)); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(11.32)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::UTC(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1604487600000)).GetRawData()); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 18); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(2020.982)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(10.23)); + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(4.32)); + ecmaRuntimeCallInfo1->SetCallArg(3, JSTaggedValue(11.32)); + ecmaRuntimeCallInfo1->SetCallArg(4, JSTaggedValue(45.1)); + ecmaRuntimeCallInfo1->SetCallArg(5, JSTaggedValue(34.321)); + ecmaRuntimeCallInfo1->SetCallArg(6, JSTaggedValue(static_cast(231))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + result1 = BuiltinsDate::UTC(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1604490334231)).GetRawData()); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(10.23)); + ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue(4.32)); + ecmaRuntimeCallInfo2->SetCallArg(2, JSTaggedValue(11.32)); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result1 = BuiltinsDate::UTC(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(-1882224000000)).GetRawData()); + + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(1994.982)); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + result1 = BuiltinsDate::UTC(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(757382400000)).GetRawData()); + + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(19999944.982)); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + result1 = BuiltinsDate::UTC(ecmaRuntimeCallInfo4.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(base::NAN_VALUE)).GetRawData()); +} + +void SetAllYearAndHours(JSThread *thread, const JSHandle &jsDate) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + // 2018 : test case + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2018))); + // 10 : test case + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(10))); + // 2, 6 : test case + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(6))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(jsDate.GetTaggedValue()); + // 18 : test case + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(18))); + // 10 : test case + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(10))); + // 2, 6 : test case + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(6))); + // 3, 111 : test case + ecmaRuntimeCallInfo1->SetCallArg(3, JSTaggedValue(static_cast(111))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + BuiltinsDate::SetHours(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); +} + +void SetAll1(JSThread *thread, const JSHandle &jsDate) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + // 1900 : test case + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1900))); + // 11 : test case + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(11))); + // 2, 31 : test case + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(31))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(jsDate.GetTaggedValue()); + // 23 : test case + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(23))); + // 54 : test case + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(54))); + // 2, 16 : test case + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(16))); + // 3, 888 : test case + ecmaRuntimeCallInfo1->SetCallArg(3, JSTaggedValue(static_cast(888))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + BuiltinsDate::SetHours(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); +} + +void SetAll2(JSThread *thread, const JSHandle &jsDate) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1901))); // 1901 : test case + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(1))); // 2 : test case + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + // 12 : test case + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(3))); // 3 : test case + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(21))); // 2, 21 : test case + ecmaRuntimeCallInfo1->SetCallArg(3, JSTaggedValue(static_cast(129))); // 3, 129 : test case + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + BuiltinsDate::SetHours(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); +} + +HWTEST_F_L0(BuiltinsDateTest, parse) +{ + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromString("2020-11-19T12:18:18.132Z"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::Parse(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1605788298132)).GetRawData()); + + JSHandle str1 = thread->GetEcmaVM()->GetFactory()->NewFromString("2020-11-19Z"); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, str1.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + result1 = BuiltinsDate::Parse(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1605744000000)).GetRawData()); + + JSHandle str2 = thread->GetEcmaVM()->GetFactory()->NewFromString("2020-11T12:18:17.231+08:00"); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetCallArg(0, str2.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result1 = BuiltinsDate::Parse(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1604204297231)).GetRawData()); + + JSHandle str3 = thread->GetEcmaVM()->GetFactory()->NewFromString("Thu Nov 19 2020 20:18:18 GMT+0800"); + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetCallArg(0, str3.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + result1 = BuiltinsDate::Parse(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1605788298000)).GetRawData()); + + JSHandle str4 = thread->GetEcmaVM()->GetFactory()->NewFromString("Thu 03 Jun 2093 04:18 GMT"); + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetCallArg(0, str4.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + result1 = BuiltinsDate::Parse(ecmaRuntimeCallInfo4.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(3894841080000)).GetRawData()); + + auto ecmaRuntimeCallInfo5 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo5->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo5->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo5->SetCallArg(0, JSTaggedValue::Null()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo5.get()); + result1 = BuiltinsDate::Parse(ecmaRuntimeCallInfo5.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(base::NAN_VALUE)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, ToDateString) +{ + JSHandle expect_value = thread->GetEcmaVM()->GetFactory()->NewFromString("Tue Nov 06 2018"); + JSHandle jsDate = JSDateCreateTest(thread); + SetAllYearAndHours(thread, jsDate); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDate::ToDateString(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expect_value)); +} + +HWTEST_F_L0(BuiltinsDateTest, ToISOString) +{ + JSHandle expect_value = thread->GetEcmaVM()->GetFactory()->NewFromString("2020-11-19T12:18:18.132Z"); + JSHandle jsDate = JSDateCreateTest(thread); + JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->SetTimeValue(thread, JSTaggedValue(1605788298132.0)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ToISOString(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result1.GetRawData()), *expect_value)); +} + +HWTEST_F_L0(BuiltinsDateTest, ToISOStringMinus) +{ + JSHandle expect_value = thread->GetEcmaVM()->GetFactory()->NewFromString("1831-12-02T21:47:18.382Z"); + JSHandle jsDate = JSDateCreateTest(thread); + JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->SetTimeValue(thread, JSTaggedValue(-4357419161618.0)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ToISOString(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result1.GetRawData()), *expect_value)); +} + +// test toJSON and toPrimitive +HWTEST_F_L0(BuiltinsDateTest, ToJSON) +{ + JSHandle expect_value = thread->GetEcmaVM()->GetFactory()->NewFromString("2020-11-19T12:18:18.132Z"); + JSHandle jsDate = JSDateCreateTest(thread); + jsDate->SetTimeValue(thread, JSTaggedValue(1605788298132.0)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ToJSON(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result1.GetRawData()), *expect_value)); +} + +HWTEST_F_L0(BuiltinsDateTest, ToJSONMinus) +{ + JSHandle expect_value = thread->GetEcmaVM()->GetFactory()->NewFromString("1831-12-02T21:47:18.382Z"); + JSHandle jsDate = JSDateCreateTest(thread); + jsDate->SetTimeValue(thread, JSTaggedValue(-4357419161618.0)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ToJSON(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result1.GetRawData()), *expect_value)); +} + +HWTEST_F_L0(BuiltinsDateTest, ToString) +{ + int localMin = 0; + CString localTime; + localMin = JSDate::GetLocalOffsetFromOS(localMin, true); + + JSHandle jsDate = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + SetAllYearAndHours(thread, jsDate); + if (static_cast(JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->GetTimeValue().GetDouble()) < + CHINA_BEFORE_1900_MS && + localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else if (localMin < 0) { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + JSTaggedValue result1 = BuiltinsDate::ToString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result1.IsString()); + TestHelper::TearDownFrame(thread, prev); + JSHandle result1_val(thread, reinterpret_cast(result1.GetRawData())); + CString str = "Tue Nov 06 2018 18:10:06 GMT" + localTime; + JSHandle str_handle = thread->GetEcmaVM()->GetFactory()->NewFromString(str); + ASSERT_TRUE(EcmaString::StringsAreEqual(*result1_val, *str_handle)); + + JSHandle js_date1 = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(js_date1.GetTaggedValue()); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + + SetAll1(thread, js_date1); + localTime = ""; + localMin = JSDate::GetLocalOffsetFromOS(localMin, true); + if (static_cast(JSDate::Cast(js_date1.GetTaggedValue().GetTaggedObject())->GetTimeValue().GetDouble()) < + CHINA_BEFORE_1900_MS && + localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else if (localMin < 0) { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + JSTaggedValue result2 = BuiltinsDate::ToString(ecmaRuntimeCallInfo1.get()); + ASSERT_TRUE(result2.IsString()); + TestHelper::TearDownFrame(thread, prev); + JSHandle result2_val(thread, reinterpret_cast(result2.GetRawData())); + str = "Mon Dec 31 1900 23:54:16 GMT" + localTime; + str_handle = thread->GetEcmaVM()->GetFactory()->NewFromString(str); + ASSERT_TRUE(EcmaString::StringsAreEqual(*result2_val, *str_handle)); + + JSHandle js_date2 = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(js_date2.GetTaggedValue()); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + + SetAll2(thread, js_date2); + localTime = ""; + localMin = JSDate::GetLocalOffsetFromOS(localMin, true); + if (static_cast(JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->GetTimeValue().GetDouble()) < + CHINA_BEFORE_1900_MS && + localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else if (localMin < 0) { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + JSTaggedValue result3 = BuiltinsDate::ToString(ecmaRuntimeCallInfo2.get()); + ASSERT_TRUE(result3.IsString()); + TestHelper::TearDownFrame(thread, prev); + JSHandle result3_val(thread, reinterpret_cast(result3.GetRawData())); + str = "Tue Jan 01 1901 00:03:21 GMT" + localTime; + str_handle = thread->GetEcmaVM()->GetFactory()->NewFromString(str); + ASSERT_TRUE(EcmaString::StringsAreEqual(*result3_val, *str_handle)); +} + +HWTEST_F_L0(BuiltinsDateTest, ToTimeString) +{ + int localMin = 0; + CString localTime; + localMin = JSDate::GetLocalOffsetFromOS(localMin, true); + + JSHandle jsDate = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + SetAllYearAndHours(thread, jsDate); + if (static_cast(JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->GetTimeValue().GetDouble()) < + CHINA_BEFORE_1900_MS && + localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else if (localMin < 0) { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + JSTaggedValue result1 = BuiltinsDate::ToTimeString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result1.IsString()); + JSHandle result1_val(thread, reinterpret_cast(result1.GetRawData())); + CString str = "18:10:06 GMT" + localTime; + JSHandle str_handle = thread->GetEcmaVM()->GetFactory()->NewFromString(str); + ASSERT_TRUE(EcmaString::StringsAreEqual(*result1_val, *str_handle)); + + JSHandle js_date1 = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(js_date1.GetTaggedValue()); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + SetAll1(thread, js_date1); + localTime = ""; + localMin = JSDate::GetLocalOffsetFromOS(localMin, true); + if (static_cast(JSDate::Cast(js_date1.GetTaggedValue().GetTaggedObject())->GetTimeValue().GetDouble()) < + CHINA_BEFORE_1900_MS && + localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else if (localMin < 0) { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + JSTaggedValue result2 = BuiltinsDate::ToTimeString(ecmaRuntimeCallInfo1.get()); + ASSERT_TRUE(result2.IsString()); + JSHandle result2_val(thread, reinterpret_cast(result2.GetRawData())); + str = "23:54:16 GMT" + localTime; + str_handle = thread->GetEcmaVM()->GetFactory()->NewFromString(str); + ASSERT_TRUE(EcmaString::StringsAreEqual(*result2_val, *str_handle)); + JSHandle js_date2 = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(js_date2.GetTaggedValue()); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + SetAll2(thread, js_date2); + localTime = ""; + localMin = JSDate::GetLocalOffsetFromOS(localMin, true); + if (static_cast(JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->GetTimeValue().GetDouble()) < + CHINA_BEFORE_1900_MS && + localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else if (localMin < 0) { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + JSTaggedValue result3 = BuiltinsDate::ToTimeString(ecmaRuntimeCallInfo2.get()); + ASSERT_TRUE(result3.IsString()); + JSHandle result3_val(thread, reinterpret_cast(result3.GetRawData())); + str = "00:03:21 GMT" + localTime; + str_handle = thread->GetEcmaVM()->GetFactory()->NewFromString(str); + ASSERT_TRUE(EcmaString::StringsAreEqual(*result3_val, *str_handle)); +} + +HWTEST_F_L0(BuiltinsDateTest, ToUTCString) +{ + JSHandle expect_value = + thread->GetEcmaVM()->GetFactory()->NewFromString("Thu, 19 Nov 2020 12:18:18 GMT"); + JSHandle jsDate = JSDateCreateTest(thread); + JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->SetTimeValue(thread, JSTaggedValue(1605788298132.0)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ToUTCString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result1.GetRawData()), *expect_value)); +} + +HWTEST_F_L0(BuiltinsDateTest, ToUTCStringMinus) +{ + JSHandle expect_value = + thread->GetEcmaVM()->GetFactory()->NewFromString("Fri, 02 Dec 1831 21:47:18 GMT"); + JSHandle jsDate = JSDateCreateTest(thread); + JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->SetTimeValue(thread, JSTaggedValue(-4357419161618.0)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ToUTCString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result1.GetRawData()), *expect_value)); +} + +HWTEST_F_L0(BuiltinsDateTest, ValueOf) +{ + JSHandle jsDate = JSDateCreateTest(thread); + JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->SetTimeValue(thread, JSTaggedValue(1605788298132.0)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ValueOf(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1605788298132)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, ValueOfMinus) +{ + JSHandle jsDate = JSDateCreateTest(thread); + JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->SetTimeValue(thread, JSTaggedValue(-4357419161618.0)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ValueOf(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(-4357419161618)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsDateTest, DateConstructor) +{ + // case1: test new target is undefined. + JSHandle jsDate = JSDateCreateTest(thread); + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle date_func(globalEnv->GetDateFunction()); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(date_func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsDate::DateConstructor(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result1.IsString()); + + // case2: length == 0 + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, jsDate.GetTaggedValue(), 4); + ecmaRuntimeCallInfo2->SetFunction(date_func.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(jsDate.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsDate::DateConstructor(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result2.IsObject()); + + // case3: length == 1 + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, jsDate.GetTaggedValue(), 6); + ecmaRuntimeCallInfo3->SetFunction(date_func.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(static_cast(2018))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + JSTaggedValue result3 = BuiltinsDate::DateConstructor(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result3.IsObject()); + + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo3.get()); + JSTaggedValue result4 = BuiltinsDate::GetFullYear(ecmaRuntimeCallInfo3.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(2018)).GetRawData()); + + // case3: length > 1 + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, jsDate.GetTaggedValue(), 8); + ecmaRuntimeCallInfo4->SetFunction(date_func.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(static_cast(2018))); + ecmaRuntimeCallInfo4->SetCallArg(1, JSTaggedValue(static_cast(10))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue result5 = BuiltinsDate::DateConstructor(ecmaRuntimeCallInfo4.get()); + ASSERT_TRUE(result5.IsObject()); + + SetAllYearAndHours(thread, jsDate); + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo4.get()); + JSTaggedValue result6 = BuiltinsDate::GetFullYear(ecmaRuntimeCallInfo4.get()); + ASSERT_EQ(result6.GetRawData(), JSTaggedValue(static_cast(2018)).GetRawData()); + JSTaggedValue result7 = BuiltinsDate::GetMonth(ecmaRuntimeCallInfo4.get()); + ASSERT_EQ(result7.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_errors_test.cpp b/ecmascript/builtins/tests/builtins_errors_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0c2d58cd400c58302aead4416a817378048fcb68 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_errors_test.cpp @@ -0,0 +1,1111 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_errors.h" + +#include "ecmascript/base/error_helper.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" + +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value-inl.h" + +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using Error = ecmascript::builtins::BuiltinsError; +using RangeError = builtins::BuiltinsRangeError; +using ReferenceError = builtins::BuiltinsReferenceError; +using TypeError = builtins::BuiltinsTypeError; +using URIError = builtins::BuiltinsURIError; +using EvalError = builtins::BuiltinsEvalError; +using SyntaxError = builtins::BuiltinsSyntaxError; +using JSType = ecmascript::JSType; + +class BuiltinsErrorsTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "BuiltinsErrorsTest SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "BuiltinsErrorsTest TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +/* + * @tc.name: GetJSErrorObject + * @tc.desc: get JSError Object + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, GetJSErrorObject) +{ + /** + * @tc.steps: step1. Create JSError object + */ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + + JSHandle handleObj = factory->GetJSError(ErrorType::TYPE_ERROR); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + /** + * @tc.steps: step2. obtain JSError object prototype chain name property and message property + */ + JSHandle msgValue( + JSObject::GetProperty(thread, JSHandle(handleObj), msgKey).GetValue()); + EXPECT_EQ(reinterpret_cast(msgValue->GetRawData()) + ->Compare(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("")).GetRawData())), + 0); + JSHandle nameValue( + JSObject::GetProperty(thread, JSHandle(handleObj), nameKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("TypeError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: GetJSErrorWithMessage + * @tc.desc: Obtains the TypeError object. + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, GetJSErrorWithMessage) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + + JSHandle handleObj = factory->GetJSError(ErrorType::TYPE_ERROR, "I am type error"); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + JSHandle msgValue( + JSObject::GetProperty(thread, JSHandle(handleObj), msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("I am type error")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + JSHandle nameValue( + JSObject::GetProperty(thread, JSHandle(handleObj), nameKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("TypeError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: ErrorNoParameterConstructor + * @tc.desc: new Error() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, ErrorNoParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Error::ErrorConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("Error")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: ErrorParameterConstructor + * @tc.desc: new Error("Hello Error!") + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, ErrorParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetErrorFunction()); + JSHandle paramMsg(factory->NewFromString("Hello Error!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Error::ErrorConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("Hello Error!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("Error")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: ErrorNoParameterToString + * @tc.desc: new Error().toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, ErrorNoParameterToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Error::ToString(ecmaRuntimeCallInfo.get()); + + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("Error")).GetRawData()) + ->Compare(reinterpret_cast(*resultHandle)), + 0); +} + +/* + * @tc.name: ErrorToString + * @tc.desc: new Error("This is Error!").toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, ErrorToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle handleMsgKey(factory->NewFromString("message")); + JSObject::SetProperty(thread, JSHandle(error), handleMsgKey, + JSHandle(thread, factory->NewFromString("This is Error!").GetTaggedValue())); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Error::ToString(ecmaRuntimeCallInfo.get()); + + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("Error: This is Error!")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: RangeErrorNoParameterConstructor + * @tc.desc: new RangeError() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, RangeErrorNoParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetRangeErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = RangeError::RangeErrorConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("")).GetRawData()) + ->Compare(reinterpret_cast(JSTaggedValue(msgValue.GetTaggedValue()).GetRawData())), + 0); + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("RangeError")).GetRawData()) + ->Compare(reinterpret_cast(JSTaggedValue(nameValue.GetTaggedValue()).GetRawData())), + 0); +} + +/* + * @tc.name: RangeErrorParameterConstructor + * @tc.desc: new RangeError("Hello RangeError!") + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, RangeErrorParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetRangeErrorFunction()); + JSHandle paramMsg(factory->NewFromString("Hello RangeError!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = RangeError::RangeErrorConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("Hello RangeError!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("RangeError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: RangeErrorNoParameterToString + * @tc.desc: new RangeError().toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, RangeErrorNoParameterToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetRangeErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = RangeError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, result); + + EXPECT_TRUE(result.IsString()); + + EXPECT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("RangeError")).GetRawData()) + ->Compare(reinterpret_cast(resultHandle->GetRawData())), + 0); +} + +/* + * @tc.name: RangeErrorToString + * @tc.desc: new RangeError("This is RangeError!").toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, RangeErrorToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetRangeErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle handleMsgKey(factory->NewFromString("message")); + JSObject::SetProperty(thread, JSHandle(error), handleMsgKey, + JSHandle(factory->NewFromString("This is RangeError!"))); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = RangeError::ToString(ecmaRuntimeCallInfo.get()); + + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(factory->NewFromString("RangeError: This is RangeError!")->Compare(*resultHandle), 0); +} + +/* + * @tc.name: ReferenceErrorNoParameterConstructor + * @tc.desc: new ReferenceError() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, ReferenceErrorNoParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetReferenceErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = ReferenceError::ReferenceErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("ReferenceError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: ReferenceErrorParameterConstructor + * @tc.desc: new ReferenceError("Hello RangeError!") + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, ReferenceErrorParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetReferenceErrorFunction()); + JSHandle paramMsg(factory->NewFromString("Hello ReferenceError!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = ReferenceError::ReferenceErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("Hello ReferenceError!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("ReferenceError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: ReferenceErrorNoParameterToString + * @tc.desc: new ReferenceError().toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, ReferenceErrorNoParameterToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetReferenceErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = ReferenceError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("ReferenceError")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: ReferenceErrorToString + * @tc.desc: new ReferenceError("This is ReferenceError!").toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, ReferenceErrorToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetReferenceErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle handleMsgKey(factory->NewFromString("message")); + JSObject::SetProperty(thread, JSHandle(error), handleMsgKey, + JSHandle(factory->NewFromString("This is ReferenceError!"))); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = ReferenceError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(factory->NewFromString("ReferenceError: This is ReferenceError!")->Compare(*resultHandle), 0); +} + +/* + * @tc.name: TypeErrorNoParameterConstructor + * @tc.desc: new TypeError() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, TypeErrorNoParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetTypeErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypeError::TypeErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + EXPECT_EQ(reinterpret_cast(JSTaggedValue(nameValue.GetTaggedValue()).GetRawData()) + ->Compare(reinterpret_cast(JSTaggedValue(nameValue.GetTaggedValue()).GetRawData())), + 0); +} + +/* + * @tc.name: TypeErrorParameterConstructor + * @tc.desc: new TypeError("Hello RangeError!") + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, TypeErrorParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetTypeErrorFunction()); + JSHandle paramMsg(factory->NewFromString("Hello TypeError!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypeError::TypeErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("Hello TypeError!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("TypeError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: TypeErrorNoParameterToString + * @tc.desc: new TypeError().toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, TypeErrorNoParameterToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetTypeErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypeError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("TypeError")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: TypeErrorToString + * @tc.desc: new TypeError("This is TypeError!").toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, TypeErrorToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetTypeErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle value(factory->NewFromString("This is TypeError!")); + JSHandle handleMsgKey(factory->NewFromString("message")); + JSObject::SetProperty(thread, JSHandle(error), handleMsgKey, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypeError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(factory->NewFromString("TypeError: This is TypeError!")->Compare(*resultHandle), 0); +} + +/* + * @tc.name: URIErrorNoParameterConstructor + * @tc.desc: new URIError() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, URIErrorNoParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetURIErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = URIError::URIErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("URIError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: URIErrorParameterConstructor + * @tc.desc: new URIError("Hello RangeError!") + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, URIErrorParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetURIErrorFunction()); + JSHandle paramMsg(factory->NewFromString("Hello URIError!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = URIError::URIErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("Hello URIError!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("URIError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: URIErrorNoParameterToString + * @tc.desc: new URIError().toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, URIErrorNoParameterToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetURIErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = URIError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + + EXPECT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("URIError")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: URIErrorToString + * @tc.desc: new URIError("This is URIError!").toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, URIErrorToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetURIErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle handleMsgKey(factory->NewFromString("message")); + JSObject::SetProperty( + thread, JSHandle(error), handleMsgKey, + JSHandle(thread, factory->NewFromString("This is URIError!").GetTaggedValue())); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = URIError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + + EXPECT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("URIError: This is URIError!")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: SyntaxErrorNoParameterConstructor + * @tc.desc: new SyntaxError() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, SyntaxErrorNoParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetSyntaxErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = SyntaxError::SyntaxErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("SyntaxError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: SyntaxErrorParameterConstructor + * @tc.desc: new SyntaxError("Hello RangeError!") + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, SyntaxErrorParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetSyntaxErrorFunction()); + JSHandle paramMsg(factory->NewFromString("Hello SyntaxError!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = SyntaxError::SyntaxErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("Hello SyntaxError!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("SyntaxError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: SyntaxErrorNoParameterToString + * @tc.desc: new SyntaxError().toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, SyntaxErrorNoParameterToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetSyntaxErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = SyntaxError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + + EXPECT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("SyntaxError")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: SyntaxErrorToString + * @tc.desc: new SyntaxError("This is SyntaxError!").toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, SyntaxErrorToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetSyntaxErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle handleMsgKey(factory->NewFromString("message")); + JSObject::SetProperty(thread, JSHandle(error), handleMsgKey, + JSHandle(factory->NewFromString("This is SyntaxError!"))); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = SyntaxError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + + EXPECT_EQ(factory->NewFromString("SyntaxError: This is SyntaxError!")->Compare(*resultHandle), 0); +} + +/* + * @tc.name: EvalErrorNoParameterConstructor + * @tc.desc: new EvalError() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, EvalErrorNoParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetEvalErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = EvalError::EvalErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("EvalError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: EvalErrorParameterConstructor + * @tc.desc: new EvalError("Hello RangeError!") + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, EvalErrorParameterConstructor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetEvalErrorFunction()); + JSHandle paramMsg(factory->NewFromString("Hello EvalError!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = EvalError::EvalErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromString("Hello EvalError!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("EvalError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: EvalErrorNoParameterToString + * @tc.desc: new EvalError().toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, EvalErrorNoParameterToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + JSHandle errorObject = env->GetEvalErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = EvalError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromString("EvalError")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: EvalErrorToString + * @tc.desc: new EvalError("This is EvalError!").toString() + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, EvalErrorToString) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetEvalErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle handleMsgKey(factory->NewFromString("message")); + JSObject::SetProperty( + thread, JSHandle(error), handleMsgKey, + JSHandle(thread, factory->NewFromString("This is EvalError!").GetTaggedValue())); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = EvalError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(factory->NewFromString("EvalError: This is EvalError!")->Compare(*resultHandle), 0); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_function_test.cpp b/ecmascript/builtins/tests/builtins_function_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..95e36eebdf72e44cb76b87aa3d8bc83592e71cbd --- /dev/null +++ b/ecmascript/builtins/tests/builtins_function_test.cpp @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_function.h" +#include "ecmascript/builtins/builtins_boolean.h" + +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_object-inl.h" + +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +using BuiltinsBase = panda::ecmascript::base::BuiltinsBase; +using JSArray = panda::ecmascript::JSArray; + +namespace panda::test { +class BuiltinsFunctionTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +// native function for test apply and call +JSTaggedValue TestFunctionApplyAndCall(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + int result = 0; + for (array_size_t index = 0; index < argv->GetArgsNumber(); ++index) { + result += BuiltinsBase::GetCallArg(argv, index)->GetInt(); + } + JSHandle thisValue(BuiltinsBase::GetThis(argv)); + + JSTaggedValue testA = + JSObject::GetProperty(thread, thisValue, + JSHandle(factory->NewFromString("test_builtins_function_a"))) + .GetValue() + .GetTaggedValue(); + JSTaggedValue testB = + JSObject::GetProperty(thread, thisValue, + JSHandle(factory->NewFromString("test_builtins_function_b"))) + .GetValue() + .GetTaggedValue(); + + result = result + testA.GetInt() + testB.GetInt(); + return BuiltinsBase::GetTaggedInt(result); +} + +// func.apply(thisArg) +HWTEST_F_L0(BuiltinsFunctionTest, FunctionPrototypeApply) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // ecma 19.2.3.1: func + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestFunctionApplyAndCall)); + + // ecma 19.2.3.1: thisArg + JSHandle thisArg(thread, env->GetGlobalObject()); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromString("test_builtins_function_a")), + JSHandle(thread, JSTaggedValue(1))); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromString("test_builtins_function_b")), + JSHandle(thread, JSTaggedValue(2))); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(func.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, (thisArg.GetTaggedValue())); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeApply(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(3).GetRawData()); + + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromString("test_builtins_function_a"))); + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromString("test_builtins_function_b"))); +} + +// func.apply(thisArg, argArray) +HWTEST_F_L0(BuiltinsFunctionTest, FunctionPrototypeApply1) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // ecma 19.2.3.1: func + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestFunctionApplyAndCall)); + + // ecma 19.2.3.1: thisArg + JSHandle thisArg(thread, env->GetGlobalObject()); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromString("test_builtins_function_a")), + JSHandle(thread, JSTaggedValue(10))); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromString("test_builtins_function_b")), + JSHandle(thread, JSTaggedValue(20))); + + // ecma 19.2.3.1: argArray + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(2))); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(30))); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(0)), desc); + + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(40))); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(1)), desc1); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(func.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, (thisArg.GetTaggedValue())); + ecmaRuntimeCallInfo->SetCallArg(1, array.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeApply(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(100).GetRawData()); + + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromString("test_builtins_function_a"))); + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromString("test_builtins_function_b"))); +} + +// target.bind(thisArg) +HWTEST_F_L0(BuiltinsFunctionTest, FunctionPrototypeBind) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle target = factory->NewJSFunction(env); + JSFunction::SetFunctionName(thread, JSHandle(target), + JSHandle(factory->NewFromString("target")), + JSHandle(thread, JSTaggedValue::Undefined())); + JSFunction::SetFunctionLength(thread, target, JSTaggedValue(2)); + + JSHandle thisArg(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, (thisArg.GetTaggedValue())); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeBind(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + + JSHandle resultFunc(thread, reinterpret_cast(result.GetRawData())); + // test BoundTarget + ASSERT_EQ(resultFunc->GetBoundTarget(), target.GetTaggedValue()); + // test BoundThis + ASSERT_EQ(resultFunc->GetBoundThis(), thisArg.GetTaggedValue()); + // test BoundArguments + JSHandle array(thread, resultFunc->GetBoundArguments()); + ASSERT_EQ(array->GetLength(), 0); + // test name property + auto globalConst = thread->GlobalConstants(); + JSHandle nameKey = globalConst->GetHandledNameString(); + JSHandle resultFuncHandle(thread, *resultFunc); + JSHandle resultName(JSObject::GetProperty(thread, resultFuncHandle, nameKey).GetValue()); + JSHandle boundTarget = factory->NewFromString("bound target"); + ASSERT_EQ(resultName->Compare(*boundTarget), 0); + // test length property + JSHandle lengthKey = globalConst->GetHandledLengthString(); + JSHandle resultLength(JSObject::GetProperty(thread, resultFuncHandle, lengthKey).GetValue()); + ASSERT_EQ(JSTaggedValue::ToNumber(thread, resultLength).GetNumber(), 2.0); +} + +// target.bind(thisArg, 123, "helloworld") +HWTEST_F_L0(BuiltinsFunctionTest, FunctionPrototypeBind1) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle target = factory->NewJSFunction(env); + JSFunction::SetFunctionName(thread, JSHandle(target), + JSHandle(factory->NewFromString("target1")), + JSHandle(thread, JSTaggedValue::Undefined())); + JSFunction::SetFunctionLength(thread, target, JSTaggedValue(5)); + + JSHandle thisArg(thread, env->GetGlobalObject()); + JSHandle str = factory->NewFromString("helloworld"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, thisArg.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(123))); + ecmaRuntimeCallInfo->SetCallArg(2, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeBind(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + + JSHandle resultFunc(thread, reinterpret_cast(result.GetRawData())); + // test BoundTarget + ASSERT_EQ(resultFunc->GetBoundTarget(), target.GetTaggedValue()); + // test BoundThis + ASSERT_EQ(resultFunc->GetBoundThis(), thisArg.GetTaggedValue()); + // test BoundArguments + JSHandle array(thread, resultFunc->GetBoundArguments()); + ASSERT_EQ(array->GetLength(), 2); + JSTaggedValue elem = array->Get(0); + JSTaggedValue elem1 = array->Get(1); + ASSERT_EQ(elem.GetRawData(), JSTaggedValue(123).GetRawData()); + + ASSERT_EQ(elem1.GetRawData(), str.GetTaggedType()); + ASSERT_TRUE(elem1.IsString()); + // test name property + auto globalConst = thread->GlobalConstants(); + JSHandle nameKey = globalConst->GetHandledNameString(); + JSHandle resultFuncHandle(thread, *resultFunc); + JSHandle resultName(JSObject::GetProperty(thread, resultFuncHandle, nameKey).GetValue()); + JSHandle rulerName = factory->NewFromString("bound target1"); + ASSERT_EQ(resultName->Compare(*rulerName), 0); + // test length property + JSHandle lengthKey = globalConst->GetHandledLengthString(); + JSHandle resultLength(JSObject::GetProperty(thread, resultFuncHandle, lengthKey).GetValue()); + // target.length is 5, (...args) length is 2 + ASSERT_EQ(JSTaggedValue::ToNumber(thread, resultLength).GetNumber(), 3.0); +} + +// target.bind(thisArg, 123, "helloworld") set target_name = EmptyString() +HWTEST_F_L0(BuiltinsFunctionTest, FunctionPrototypeBind2) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle target = factory->NewJSFunction(env); + PropertyDescriptor nameDesc(thread, JSHandle(thread, JSTaggedValue(123)), false, false, true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(target), + thread->GlobalConstants()->GetHandledNameString(), nameDesc); + JSFunction::SetFunctionLength(thread, target, JSTaggedValue(5)); + + JSHandle thisArg(thread, env->GetGlobalObject()); + JSHandle str = factory->NewFromString("helloworld"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, (thisArg.GetTaggedValue())); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(123))); + ecmaRuntimeCallInfo->SetCallArg(2, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeBind(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + + JSHandle resultFunc(thread, reinterpret_cast(result.GetRawData())); + // test BoundTarget + ASSERT_EQ(resultFunc->GetBoundTarget(), target.GetTaggedValue()); + // test BoundThis + ASSERT_EQ(resultFunc->GetBoundThis(), thisArg.GetTaggedValue()); + // test BoundArguments + JSHandle array(thread, resultFunc->GetBoundArguments()); + ASSERT_EQ(array->GetLength(), 2); + JSTaggedValue elem = array->Get(0); + JSTaggedValue elem1 = array->Get(1); + ASSERT_EQ(elem.GetRawData(), JSTaggedValue(123).GetRawData()); + + ASSERT_EQ(elem1.GetRawData(), str.GetTaggedType()); + ASSERT_TRUE(elem1.IsString()); + // test name property + auto globalConst = thread->GlobalConstants(); + JSHandle nameKey = globalConst->GetHandledNameString(); + JSHandle resultFuncHandle(resultFunc); + JSHandle resultName(JSObject::GetProperty(thread, resultFuncHandle, nameKey).GetValue()); + JSHandle rulerName = factory->NewFromString("bound "); + ASSERT_EQ(resultName->Compare(*rulerName), 0); + // test length property + JSHandle lengthKey = globalConst->GetHandledLengthString(); + JSHandle resultLength(JSObject::GetProperty(thread, resultFuncHandle, lengthKey).GetValue()); + // target.length is 5, (...args) length is 2 + ASSERT_EQ(JSTaggedValue::ToNumber(thread, resultLength).GetNumber(), 3.0); +} + +// func.call(thisArg) +HWTEST_F_L0(BuiltinsFunctionTest, FunctionPrototypeCall) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // ecma 19.2.3.3: func + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestFunctionApplyAndCall)); + + // ecma 19.2.3.3: thisArg + JSHandle thisArg(thread, env->GetGlobalObject()); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromString("test_builtins_function_a")), + JSHandle(thread, JSTaggedValue(1))); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromString("test_builtins_function_b")), + JSHandle(thread, JSTaggedValue(2))); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(func.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, (thisArg.GetTaggedValue())); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeCall(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(3).GetRawData()); + + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromString("test_builtins_function_a"))); + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromString("test_builtins_function_b"))); +} + +// func.call(thisArg, 123, 456, 789) +HWTEST_F_L0(BuiltinsFunctionTest, FunctionPrototypeCall1) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // ecma 19.2.3.3: func + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestFunctionApplyAndCall)); + + // ecma 19.2.3.3: thisArg + JSHandle thisArg(thread, env->GetGlobalObject()); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromString("test_builtins_function_a")), + JSHandle(thread, JSTaggedValue(1))); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromString("test_builtins_function_b")), + JSHandle(thread, JSTaggedValue(2))); + + // func thisArg ...args + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(func.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, (thisArg.GetTaggedValue())); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(123))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(456))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(static_cast(789))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeCall(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(1371).GetRawData()); + + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromString("test_builtins_function_a"))); + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromString("test_builtins_function_b"))); +} + +HWTEST_F_L0(BuiltinsFunctionTest, FunctionPrototypeHasInstance) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle booleanCtor(env->GetBooleanFunction()); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*booleanCtor), 6); + ecmaRuntimeCallInfo1->SetFunction(booleanCtor.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(123))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle booleanInstance(thread, result); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(booleanCtor.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, booleanInstance.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + EXPECT_TRUE(BuiltinsFunction::FunctionPrototypeHasInstance(ecmaRuntimeCallInfo2.get()).GetRawData()); + TestHelper::TearDownFrame(thread, prev); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_iterator_test.cpp b/ecmascript/builtins/tests/builtins_iterator_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ee7541757a08a5722d1935b3aa670df9f8707159 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_iterator_test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_iterator.h" + +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_thread.h" + +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsIteratorTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_json_test.cpp b/ecmascript/builtins/tests/builtins_json_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1bc972607635becb58a48f9e9e7cdedf0ade71fc --- /dev/null +++ b/ecmascript/builtins/tests/builtins_json_test.cpp @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "algorithm" +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/builtins/builtins_errors.h" +#include "ecmascript/builtins/builtins_json.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsJsonTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestForParse(EcmaRuntimeCallInfo *argv) + { + array_size_t argc = argv->GetArgsNumber(); + if (argc > 0) { + } + JSTaggedValue key = GetCallArg(argv, 0).GetTaggedValue(); + if (key.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + JSTaggedValue value = GetCallArg(argv, 1).GetTaggedValue(); + if (value.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + + return JSTaggedValue(value); + } + + static JSTaggedValue TestForParse1(EcmaRuntimeCallInfo *argv) + { + array_size_t argc = argv->GetArgsNumber(); + if (argc > 0) { + } + return JSTaggedValue::Undefined(); + } + + static JSTaggedValue TestForStringfy(EcmaRuntimeCallInfo *argv) + { + array_size_t argc = argv->GetArgsNumber(); + if (argc > 0) { + JSTaggedValue key = GetCallArg(argv, 0).GetTaggedValue(); + if (key.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + JSTaggedValue value = GetCallArg(argv, 1).GetTaggedValue(); + if (value.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + return JSTaggedValue(value); + } + + return JSTaggedValue::Undefined(); + } + }; +}; + +JSTaggedValue CreateBuiltinJSObject1(JSThread *thread, const CString keyCStr) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + JSHandle objectFunc(globalEnv->GetObjectFunction()); + + JSHandle jsobject(factory->NewJSObjectByConstructor(JSHandle(objectFunc), objectFunc)); + EXPECT_TRUE(*jsobject != nullptr); + + JSHandle key(factory->NewFromString(&keyCStr[0])); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(jsobject), key, value); + + CString str2 = "y"; + JSHandle key2(factory->NewFromString(str2)); + JSHandle value2(thread, JSTaggedValue(2.5)); // 2.5 : test case + JSObject::SetProperty(thread, JSHandle(jsobject), key2, value2); + + CString str3 = "z"; + JSHandle key3(factory->NewFromString(str3)); + JSHandle value3(factory->NewFromString("abc")); + JSObject::SetProperty(thread, JSHandle(jsobject), key3, value3); + + return jsobject.GetTaggedValue(); +} +// Math.abs(-10) + +HWTEST_F_L0(BuiltinsJsonTest, Parse10) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle msg(factory->NewFromString( + "\t\r \n{\t\r \n \"property\"\t\r \n:\t\r \n{\t\r \n}\t\r \n,\t\r \n \"prop2\"\t\r \n:\t\r \n [\t\r \ntrue\t\r " + "\n,\t\r \nnull\t\r \n,123.456\t\r \n] \t\r \n}\t\r \n")); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Parse(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); +} + +HWTEST_F_L0(BuiltinsJsonTest, Parse21) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle msg(factory->NewFromString("[100,2.5,\"abc\"]")); + + JSHandle handleFunc = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForParse)); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handleFunc.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Parse(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); +} + +HWTEST_F_L0(BuiltinsJsonTest, Parse) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + + JSHandle msg(factory->NewFromString("[100,2.5,\"abc\"]")); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Parse(ecmaRuntimeCallInfo.get()); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + JSHandle valueHandle(thread, value); + JSHandle lenResult = + JSObject::GetProperty(thread, JSHandle(valueHandle), lengthKeyHandle).GetValue(); + uint32_t length = JSTaggedValue::ToLength(thread, lenResult).ToUint32(); + EXPECT_EQ(length, 3); +} + +HWTEST_F_L0(BuiltinsJsonTest, Parse2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle msg(factory->NewFromString("{\"epf\":100,\"key1\":200}")); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Parse(ecmaRuntimeCallInfo.get()); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + JSHandle valueHandle(thread, value); + + JSHandle nameList(JSObject::EnumerableOwnNames(thread, valueHandle)); + JSHandle nameResult = JSArray::CreateArrayFromList(thread, nameList); + + JSHandle handleKey(nameResult); + JSHandle lengthKey(factory->NewFromString("length")); + JSHandle lenResult = JSObject::GetProperty(thread, handleKey, lengthKey).GetValue(); + uint32_t length = JSTaggedValue::ToLength(thread, lenResult).ToUint32(); + ASSERT_EQ(length, 2); +} + +HWTEST_F_L0(BuiltinsJsonTest, Stringify11) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject1(thread, "x")); + JSHandle handleFunc = + factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForStringfy)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handleFunc.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} + +HWTEST_F_L0(BuiltinsJsonTest, Stringify12) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject1(thread, "x")); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle handleFunc = + factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForStringfy)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handleFunc.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} + +HWTEST_F_L0(BuiltinsJsonTest, Stringify13) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject1(thread, "x")); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle handleFunc = + factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForStringfy)); + JSHandle msg(factory->NewFromString("tttt")); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handleFunc.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} + +HWTEST_F_L0(BuiltinsJsonTest, Stringify14) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject1(thread, "x")); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + + JSHandle obj1(thread, arr); + JSHandle key0(thread, JSTaggedValue(0)); + JSHandle value0(factory->NewFromString("x")); + JSObject::SetProperty(thread, JSHandle(obj), key0, value0); + JSHandle key1(thread, JSTaggedValue(1)); + JSHandle value1(factory->NewFromString("z")); + JSObject::SetProperty(thread, JSHandle(obj), key1, value1); + + JSHandle msg(factory->NewFromString("tttt")); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, obj1.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} + +HWTEST_F_L0(BuiltinsJsonTest, Stringify) +{ + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject1(thread, "x")); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} + +HWTEST_F_L0(BuiltinsJsonTest, Stringify1) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVM->GetFactory(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + JSHandle key0(thread, JSTaggedValue(0)); + + JSHandle value(factory->NewFromString("def")); + JSObject::SetProperty(thread, JSHandle(obj), key0, value); + + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(200)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + + JSHandle key2(thread, JSTaggedValue(2)); + JSHandle value2(factory->NewFromString("abc")); + JSObject::SetProperty(thread, JSHandle(obj), key2, value2); + + JSHandle handleFunc = + factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForStringfy)); + JSHandle msg(factory->NewFromString("tttt")); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handleFunc.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} + +HWTEST_F_L0(BuiltinsJsonTest, Stringify2) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + // 2.5 : test case + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2.5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + // 2 : test case + JSHandle key2(thread, JSTaggedValue(2)); + JSHandle value2(factory->NewFromString("abc")); + JSObject::SetProperty(thread, JSHandle(obj), key2, value2); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_map_test.cpp b/ecmascript/builtins/tests/builtins_map_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6ea43bf535f597cc0b2c9c62e2100cf405717875 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_map_test.cpp @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/builtins/builtins_map.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_map.h" +#include "ecmascript/js_map_iterator.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tests/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using BuiltinsMap = ecmascript::builtins::BuiltinsMap; +using JSMap = ecmascript::JSMap; + +class BuiltinsMapTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestFunc(EcmaRuntimeCallInfo *argv) + { + int num = GetCallArg(argv, 0)->GetInt(); + JSArray *jsArray = JSArray::Cast(GetThis(argv)->GetTaggedObject()); + int length = jsArray->GetArrayLength() + num; + jsArray->SetArrayLength(argv->GetThread(), length); + return JSTaggedValue::Undefined(); + } + }; +}; + +JSMap *CreateBuiltinsMap(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsMapFunction()); + // 4 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 4); + ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMap::MapConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + JSMap *jsMap = JSMap::Cast(reinterpret_cast(result.GetRawData())); + return jsMap; +} +// new Map("abrupt").toString() +HWTEST_F_L0(BuiltinsMapTest, CreateAndGetSize) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsMapFunction()); + JSHandle map(thread, CreateBuiltinsMap(thread)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMap::GetSize(ecmaRuntimeCallInfo.get()); + + EXPECT_EQ(result.GetRawData(), JSTaggedValue(0).GetRawData()); + } + JSHandle array(factory->NewTaggedArray(5)); + for (int i = 0; i < 5; i++) { + JSHandle internal_array(factory->NewTaggedArray(2)); + internal_array->Set(thread, 0, JSTaggedValue(i)); + internal_array->Set(thread, 1, JSTaggedValue(i)); + auto arr = JSArray::CreateArrayFromList(thread, internal_array); + array->Set(thread, i, arr); + } + JSHandle values = JSArray::CreateArrayFromList(thread, array); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, values.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetNewTarget(newTarget.GetTaggedValue()); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsMap::MapConstructor(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(JSMap::Cast(reinterpret_cast(result1.GetRawData()))->GetSize(), 5); + } +} + +HWTEST_F_L0(BuiltinsMapTest, SetAndHas) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // create jsMap + JSHandle map(thread, CreateBuiltinsMap(thread)); + JSHandle key(factory->NewFromString("key")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + + JSMap *jsMap; + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsMap::Has(ecmaRuntimeCallInfo.get()); + + EXPECT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData()); + + // test Set() + JSTaggedValue result2 = BuiltinsMap::Set(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result2.IsECMAObject()); + jsMap = JSMap::Cast(reinterpret_cast(result2.GetRawData())); + EXPECT_EQ(jsMap->GetSize(), 1); + } + + // test Has() + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(jsMap)); + ecmaRuntimeCallInfo1->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(1))); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result3 = BuiltinsMap::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + } +} + +HWTEST_F_L0(BuiltinsMapTest, ForEach) +{ + // generate a map has 5 entries{key1:0,key2:1,key3:2,key4:3,key5:4} + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle map(thread, CreateBuiltinsMap(thread)); + char keyArray[] = "key0"; + for (int i = 0; i < 5; i++) { + keyArray[3] = '1' + i; + JSHandle key(factory->NewFromString(keyArray)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(i))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsMap::Set(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result1.IsECMAObject()); + JSMap *jsMap = JSMap::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsMap->GetSize(), i + 1); + } + // test foreach; + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsMap::ForEach(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + EXPECT_EQ(jsArray->GetArrayLength(), 10); +} + +HWTEST_F_L0(BuiltinsMapTest, DeleteAndRemove) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // create jsMap + JSHandle map(thread, CreateBuiltinsMap(thread)); + + // add 40 keys + char keyArray[] = "key0"; + for (int i = 0; i < 40; i++) { + keyArray[3] = '1' + i; + JSHandle key(thread, factory->NewFromString(keyArray).GetTaggedValue()); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(i))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsMap::Set(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result1.IsECMAObject()); + JSMap *jsMap = JSMap::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsMap->GetSize(), i + 1); + } + // whether jsMap has delete key + keyArray[3] = '1' + 8; + JSHandle deleteKey(factory->NewFromString(keyArray)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, deleteKey.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsMap::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); + + // delete + JSTaggedValue result3 = BuiltinsMap::Delete(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + + // check deleteKey is deleted + JSTaggedValue result4 = BuiltinsMap::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); + JSTaggedValue result5 = BuiltinsMap::GetSize(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result5.GetRawData(), JSTaggedValue(39).GetRawData()); + + // clear + JSTaggedValue result6 = BuiltinsMap::Clear(ecmaRuntimeCallInfo1.get()); + EXPECT_EQ(result6.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + EXPECT_EQ(map->GetSize(), 0); +} + +HWTEST_F_L0(BuiltinsMapTest, Species) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle map(thread, CreateBuiltinsMap(thread)); + + // test species + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + EXPECT_TRUE(!speciesSymbol.GetTaggedValue().IsUndefined()); + + JSHandle newTarget(env->GetBuiltinsMapFunction()); + + JSTaggedValue value = + JSObject::GetProperty(thread, JSHandle(newTarget), speciesSymbol).GetValue().GetTaggedValue(); + JSHandle valueHandle(thread, value); + EXPECT_EQ(value, newTarget.GetTaggedValue()); + + // to string tag + JSHandle toStringTagSymbol = env->GetToStringTagSymbol(); + JSHandle stringTag(JSObject::GetProperty(thread, map, toStringTagSymbol).GetValue()); + JSHandle str = factory->NewFromString("Map"); + EXPECT_TRUE(!stringTag.GetTaggedValue().IsUndefined()); + EXPECT_TRUE(EcmaString::StringsAreEqual(*str, *stringTag)); + + JSHandle constructor = JSHandle::Cast(JSTaggedValue::ToObject(thread, valueHandle)); + EXPECT_EQ(JSHandle(map)->GetPrototype(thread), constructor->GetFunctionPrototype()); + + JSHandle key1(factory->NewFromString("set")); + JSTaggedValue value1 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value1.IsUndefined()); + + JSHandle key2(factory->NewFromString("has")); + JSTaggedValue value2 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value2.IsUndefined()); + + JSHandle key3(factory->NewFromString("clear")); + JSTaggedValue value3 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value3.IsUndefined()); + + JSHandle key4(factory->NewFromString("size")); + JSTaggedValue value4 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value4.IsUndefined()); + + JSHandle key5(factory->NewFromString("delete")); + JSTaggedValue value5 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value5.IsUndefined()); + + JSHandle key6(factory->NewFromString("forEach")); + JSTaggedValue value6 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value6.IsUndefined()); + + JSHandle key7(factory->NewFromString("get")); + JSTaggedValue value7 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value7.IsUndefined()); +} + +HWTEST_F_L0(BuiltinsMapTest, GetIterator) +{ + JSHandle map(thread, CreateBuiltinsMap(thread)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue()); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + // test Values() + JSTaggedValue result = BuiltinsMap::Values(ecmaRuntimeCallInfo.get()); + JSHandle iter(thread, result); + EXPECT_TRUE(iter->IsJSMapIterator()); + EXPECT_EQ(IterationKind::VALUE, IterationKind(iter->GetIterationKind().GetInt())); + EXPECT_EQ(JSMap::Cast(map.GetTaggedValue().GetTaggedObject())->GetLinkedMap(), iter->GetIteratedMap()); + + // test Keys() + JSTaggedValue result1 = BuiltinsMap::Keys(ecmaRuntimeCallInfo.get()); + JSHandle iter1(thread, result1); + EXPECT_TRUE(iter1->IsJSMapIterator()); + EXPECT_EQ(IterationKind::KEY, IterationKind(iter1->GetIterationKind().GetInt())); + + // test entries() + JSTaggedValue result2 = BuiltinsMap::Entries(ecmaRuntimeCallInfo.get()); + JSHandle iter2(thread, result2); + EXPECT_TRUE(iter2->IsJSMapIterator()); + EXPECT_EQ(IterationKind::KEY_AND_VALUE, IterationKind(iter2->GetIterationKind().GetInt())); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_math_test.cpp b/ecmascript/builtins/tests/builtins_math_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f475155473700a6f69725c20f0eb3f718e9e9a86 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_math_test.cpp @@ -0,0 +1,3926 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/number_helper.h" +#include "ecmascript/builtins/builtins_math.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +using namespace panda::ecmascript::base; + +namespace panda::test { +class BuiltinsMathTest : public testing::Test { +public: + // Workaround: Avoid thread local leak [F/runtime: cannot create thread specific key for __cxa_get_globals()] + static void SetUpTestCase() + { + TestHelper::CreateEcmaVMWithScope(instance_, thread_, scope_); + } + + static void TearDownTestCase() + { + TestHelper::DestroyEcmaVMWithScope(instance_, scope_); + } + + static PandaVM *instance_; + static EcmaHandleScope *scope_; + static JSThread *thread_; +}; +PandaVM *BuiltinsMathTest::instance_ = nullptr; +EcmaHandleScope *BuiltinsMathTest::scope_ = nullptr; +JSThread *BuiltinsMathTest::thread_ = nullptr; + +// Math.abs(-10) +HWTEST_F_L0(BuiltinsMathTest, Abs) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(10); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(10) +HWTEST_F_L0(BuiltinsMathTest, Abs_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(10); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(0) +HWTEST_F_L0(BuiltinsMathTest, Abs_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(null) +HWTEST_F_L0(BuiltinsMathTest, Abs_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs("hello") +HWTEST_F_L0(BuiltinsMathTest, Abs_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("helloworld"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(Number.MAX_VALUE + 1) +HWTEST_F_L0(BuiltinsMathTest, Abs_5) +{ + const double testValue = base::MAX_VALUE + 1; + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(testValue)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::MAX_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(Number.MIN_VALUE) +HWTEST_F_L0(BuiltinsMathTest, Abs_6) +{ + const double testValue = base::MIN_VALUE + 1; + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(testValue)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(Number.POSITIVE_INFINITY + 1) +HWTEST_F_L0(BuiltinsMathTest, Abs_7) +{ + const double testValue = base::POSITIVE_INFINITY + 1; + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(testValue)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(Number.NEGATIVE_INFINITY - 1) +HWTEST_F_L0(BuiltinsMathTest, Abs_8) +{ + const double testValue = -base::POSITIVE_INFINITY - 1; + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(testValue)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(Number.NAN_VALUE) +HWTEST_F_L0(BuiltinsMathTest, Abs_9) +{ + const double testValue = base::NAN_VALUE; + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(testValue)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(VALUE_UNDEFINED) +HWTEST_F_L0(BuiltinsMathTest, Abs_10) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(true) +HWTEST_F_L0(BuiltinsMathTest, Abs_11) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(1); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(false) +HWTEST_F_L0(BuiltinsMathTest, Abs_12) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(hole) +HWTEST_F_L0(BuiltinsMathTest, Abs_13) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Hole()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs("100.12") +HWTEST_F_L0(BuiltinsMathTest, Abs_14) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("100.12"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(100.12); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(-1) +HWTEST_F_L0(BuiltinsMathTest, Acos) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(BuiltinsMath::PI); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(1) +HWTEST_F_L0(BuiltinsMathTest, Acos_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(-1.5) +HWTEST_F_L0(BuiltinsMathTest, Acos_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(null) +HWTEST_F_L0(BuiltinsMathTest, Acos_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(UNDEFINED) +HWTEST_F_L0(BuiltinsMathTest, Acos_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(true) +HWTEST_F_L0(BuiltinsMathTest, Acos_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(false) +HWTEST_F_L0(BuiltinsMathTest, Acos_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos("0.1") +HWTEST_F_L0(BuiltinsMathTest, Acos_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.4706289056333368); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos("") +HWTEST_F_L0(BuiltinsMathTest, Acos_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Acos_9) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(1.1) +HWTEST_F_L0(BuiltinsMathTest, Acosh) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(1.1)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.4435682543851154); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(0.5) +HWTEST_F_L0(BuiltinsMathTest, Acosh_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(base::POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Acosh_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(null) +HWTEST_F_L0(BuiltinsMathTest, Acosh_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(VALUE_UNDEFINED) +HWTEST_F_L0(BuiltinsMathTest, Acosh_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(true) +HWTEST_F_L0(BuiltinsMathTest, Acosh_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(false) +HWTEST_F_L0(BuiltinsMathTest, Acosh_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(hole) +HWTEST_F_L0(BuiltinsMathTest, Acosh_7) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Hole()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh("1") +HWTEST_F_L0(BuiltinsMathTest, Acosh_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh("") +HWTEST_F_L0(BuiltinsMathTest, Acosh_9) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Acosh_10) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(-1) +HWTEST_F_L0(BuiltinsMathTest, Asin) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(1) +HWTEST_F_L0(BuiltinsMathTest, Asin_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Asin_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(null) +HWTEST_F_L0(BuiltinsMathTest, Asin_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(UNDEFINED) +HWTEST_F_L0(BuiltinsMathTest, Asin_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(true) +HWTEST_F_L0(BuiltinsMathTest, Asin_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(false) +HWTEST_F_L0(BuiltinsMathTest, Asin_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(""") +HWTEST_F_L0(BuiltinsMathTest, Asin_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin("1") +HWTEST_F_L0(BuiltinsMathTest, Asin_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(-1) +HWTEST_F_L0(BuiltinsMathTest, Asinh) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.881373587019543); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(1) +HWTEST_F_L0(BuiltinsMathTest, Asinh_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.881373587019543); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(null) +HWTEST_F_L0(BuiltinsMathTest, Asinh_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Asinh_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(NEGATIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Asinh_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(true) +HWTEST_F_L0(BuiltinsMathTest, Asinh_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.881373587019543); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(false) +HWTEST_F_L0(BuiltinsMathTest, Asinh_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh("") +HWTEST_F_L0(BuiltinsMathTest, Asinh_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh("-5.7") +HWTEST_F_L0(BuiltinsMathTest, Asinh_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("-5.7"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-2.44122070725561); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(-1) +HWTEST_F_L0(BuiltinsMathTest, Atan) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.7853981633974483); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(1) +HWTEST_F_L0(BuiltinsMathTest, Atan_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.7853981633974483); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(null) +HWTEST_F_L0(BuiltinsMathTest, Atan_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Atan_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Atan_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(BuiltinsMath::PI / 2); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(true) +HWTEST_F_L0(BuiltinsMathTest, Atan_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.7853981633974483); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(false) +HWTEST_F_L0(BuiltinsMathTest, Atan_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan("") +HWTEST_F_L0(BuiltinsMathTest, Atan_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString(" "); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan("-1") +HWTEST_F_L0(BuiltinsMathTest, Atan_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("-1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.7853981633974483); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(-1) +HWTEST_F_L0(BuiltinsMathTest, Atanh) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(1) +HWTEST_F_L0(BuiltinsMathTest, Atanh_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(null) +HWTEST_F_L0(BuiltinsMathTest, Atanh_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Atanh_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(1.5) +HWTEST_F_L0(BuiltinsMathTest, Atanh_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(true) +HWTEST_F_L0(BuiltinsMathTest, Atanh_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(false) +HWTEST_F_L0(BuiltinsMathTest, Atanh_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh("") +HWTEST_F_L0(BuiltinsMathTest, Atanh_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString(" "); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh("-1") +HWTEST_F_L0(BuiltinsMathTest, Atanh_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("-1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(NaN, 1.5) +HWTEST_F_L0(BuiltinsMathTest, Atan2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::NAN_VALUE)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(-1, 1.5) +HWTEST_F_L0(BuiltinsMathTest, Atan2_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.5880026035475675); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(1, -0) +HWTEST_F_L0(BuiltinsMathTest, Atan2_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(BuiltinsMath::PI / 2); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(0, 1) +HWTEST_F_L0(BuiltinsMathTest, Atan2_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(0, -0) +HWTEST_F_L0(BuiltinsMathTest, Atan2_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(BuiltinsMath::PI); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(-0, 0) +HWTEST_F_L0(BuiltinsMathTest, Atan2_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(-0, -0) +HWTEST_F_L0(BuiltinsMathTest, Atan2_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-BuiltinsMath::PI); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(true, false) +HWTEST_F_L0(BuiltinsMathTest, Atan2_7) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(false, true) +HWTEST_F_L0(BuiltinsMathTest, Atan2_8) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2("-1","") +HWTEST_F_L0(BuiltinsMathTest, Atan2_9) +{ + JSHandle test_1 = thread_->GetEcmaVM()->GetFactory()->NewFromString("-1"); + JSHandle test_2 = thread_->GetEcmaVM()->GetFactory()->NewFromString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test_1.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, test_2.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2("0.23","0.72") +HWTEST_F_L0(BuiltinsMathTest, Atan2_10) +{ + JSHandle test_1 = thread_->GetEcmaVM()->GetFactory()->NewFromString("0.23"); + JSHandle test_2 = thread_->GetEcmaVM()->GetFactory()->NewFromString("0.72"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test_1.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, test_2.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.3091989123270746); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(-NaN, 1.5) +HWTEST_F_L0(BuiltinsMathTest, Atan2_11) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(0) +HWTEST_F_L0(BuiltinsMathTest, Cbrt) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(-0) +HWTEST_F_L0(BuiltinsMathTest, Cbrt_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(NEGATIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Cbrt_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Cbrt_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(VALUE_UNDEFINED) +HWTEST_F_L0(BuiltinsMathTest, Cbrt_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(true) +HWTEST_F_L0(BuiltinsMathTest, Cbrt_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(false) +HWTEST_F_L0(BuiltinsMathTest, Cbrt_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt("") +HWTEST_F_L0(BuiltinsMathTest, Cbrt_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString(" "); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt("1.23") +HWTEST_F_L0(BuiltinsMathTest, Cbrt_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("1.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0714412696907731); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Cbrt_9) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(3.25) +HWTEST_F_L0(BuiltinsMathTest, Ceil) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(3.25)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(4.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Ceil_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(-0.0) +HWTEST_F_L0(BuiltinsMathTest, Ceil_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(null) +HWTEST_F_L0(BuiltinsMathTest, Ceil_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(0) +HWTEST_F_L0(BuiltinsMathTest, Ceil_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(true) +HWTEST_F_L0(BuiltinsMathTest, Ceil_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(false) +HWTEST_F_L0(BuiltinsMathTest, Ceil_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil("") +HWTEST_F_L0(BuiltinsMathTest, Ceil_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil("3.23") +HWTEST_F_L0(BuiltinsMathTest, Ceil_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(4.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Ceil_9) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos(0) +HWTEST_F_L0(BuiltinsMathTest, Cos) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos(-NAN) +HWTEST_F_L0(BuiltinsMathTest, Cos_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Cos_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos(-POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Cos_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos(true) +HWTEST_F_L0(BuiltinsMathTest, Cos_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.5403023058681398); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos(false) +HWTEST_F_L0(BuiltinsMathTest, Cos_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos("") +HWTEST_F_L0(BuiltinsMathTest, Cos_6) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos("3.23") +HWTEST_F_L0(BuiltinsMathTest, Cos_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.9960946152060809); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh(0) +HWTEST_F_L0(BuiltinsMathTest, Cosh) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh(-NAN) +HWTEST_F_L0(BuiltinsMathTest, Cosh_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Cosh_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh(-POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Cosh_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh(true) +HWTEST_F_L0(BuiltinsMathTest, Cosh_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5430806348152437); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh(false) +HWTEST_F_L0(BuiltinsMathTest, Cosh_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh("") +HWTEST_F_L0(BuiltinsMathTest, Cosh_6) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString(" "); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh("3.23") +HWTEST_F_L0(BuiltinsMathTest, Cosh_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(12.659607234875645); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp(0) +HWTEST_F_L0(BuiltinsMathTest, Exp) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp(-NAN) +HWTEST_F_L0(BuiltinsMathTest, Exp_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Exp_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp(-POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Exp_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp(true) +HWTEST_F_L0(BuiltinsMathTest, Exp_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(2.718281828459045); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp(false) +HWTEST_F_L0(BuiltinsMathTest, Exp_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp("") +HWTEST_F_L0(BuiltinsMathTest, Exp_6) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp("-3.23") +HWTEST_F_L0(BuiltinsMathTest, Exp_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("-3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.039557498788398725); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Expm1(0) +HWTEST_F_L0(BuiltinsMathTest, Expm1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Expm1(-0.0) +HWTEST_F_L0(BuiltinsMathTest, Expm1_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Expm1(-NAN) +HWTEST_F_L0(BuiltinsMathTest, Expm1_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Expm1(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Expm1_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Expm1(-POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Expm1_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.expm1(true) +HWTEST_F_L0(BuiltinsMathTest, Expm1_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + double expect = 1.718281828459045; + ASSERT_TRUE(result.IsDouble()); + ASSERT_TRUE(std::abs(result.GetDouble() - expect) < 0.00000001); +} + +// Math.expm1(false) +HWTEST_F_L0(BuiltinsMathTest, Expm1_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.expm1("") +HWTEST_F_L0(BuiltinsMathTest, Expm1_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString(" "); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.expm1("-3.23") +HWTEST_F_L0(BuiltinsMathTest, Expm1_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("-3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.9604425012116012); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.expm1("0x12") +HWTEST_F_L0(BuiltinsMathTest, Expm1_9) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("0x12"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(65659968.13733051); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.floor(-0.0) +HWTEST_F_L0(BuiltinsMathTest, Floor) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Floor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.floor(-NAN) +HWTEST_F_L0(BuiltinsMathTest, Floor_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Floor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.floor(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Floor_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Floor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.floor(true) +HWTEST_F_L0(BuiltinsMathTest, Floor_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Floor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.floor("-3.23") +HWTEST_F_L0(BuiltinsMathTest, Floor_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("-3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Floor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-4.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log(-0.0) +HWTEST_F_L0(BuiltinsMathTest, Log) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log(-NAN) +HWTEST_F_L0(BuiltinsMathTest, Log_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Log_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log(true) +HWTEST_F_L0(BuiltinsMathTest, Log_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log("-3.23") +HWTEST_F_L0(BuiltinsMathTest, Log_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("-3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log(0.12) +HWTEST_F_L0(BuiltinsMathTest, Log_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.12)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-2.120263536200091); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log1p(-0.0) +HWTEST_F_L0(BuiltinsMathTest, Log1p) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log1p(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log1p(-NAN) +HWTEST_F_L0(BuiltinsMathTest, Log1p_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log1p(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log1p(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Log1p_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log1p(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log1p(true) +HWTEST_F_L0(BuiltinsMathTest, Log1p_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log1p(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.6931471805599453); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log1p("-3.23") +HWTEST_F_L0(BuiltinsMathTest, Log1p_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("-3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log1p(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log1p(0.12) +HWTEST_F_L0(BuiltinsMathTest, Log1p_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.12)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log1p(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.11332868530700317); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log10(-0.0) +HWTEST_F_L0(BuiltinsMathTest, Log10) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log10(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Log10(-NAN) +HWTEST_F_L0(BuiltinsMathTest, Log10_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log10(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Log10(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Log10_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log10(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Log10(true) +HWTEST_F_L0(BuiltinsMathTest, Log10_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log10(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Log10("2") +HWTEST_F_L0(BuiltinsMathTest, Log10_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("2"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log10(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.3010299956639812); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Log10(0.12) +HWTEST_F_L0(BuiltinsMathTest, Log10_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.12)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log10(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.9208187539523752); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log2(-0.0) +HWTEST_F_L0(BuiltinsMathTest, Log2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log2(-NAN) +HWTEST_F_L0(BuiltinsMathTest, Log2_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log2(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Log2_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log2(true) +HWTEST_F_L0(BuiltinsMathTest, Log2_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log2("2") +HWTEST_F_L0(BuiltinsMathTest, Log2_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("2"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log2(1) +HWTEST_F_L0(BuiltinsMathTest, Log2_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Max(NaN,1,POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Max) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::NAN_VALUE)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Max(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Max() +HWTEST_F_L0(BuiltinsMathTest, Max_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Max(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Max("3",100,2.5) +HWTEST_F_L0(BuiltinsMathTest, Max_2) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("3"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(100))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(2.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Max(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(100); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Max(3,"100",-101.5) +HWTEST_F_L0(BuiltinsMathTest, Max_3) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("100"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo->SetCallArg(1, test.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(-101.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Max(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(100.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Max(-3,"-100",true) +HWTEST_F_L0(BuiltinsMathTest, Max_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("-100"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-3))); + ecmaRuntimeCallInfo->SetCallArg(1, test.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Max(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(1); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.min(NaN,1,POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Min) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::NAN_VALUE)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Min(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.min() +HWTEST_F_L0(BuiltinsMathTest, Min_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Min(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.min("3",100,2.5) +HWTEST_F_L0(BuiltinsMathTest, Min_2) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("3"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(100))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(2.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Min(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(2.5); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.min(3,"100",-101.5) +HWTEST_F_L0(BuiltinsMathTest, Min_3) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("100"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo->SetCallArg(1, test.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(-101.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Min(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-101.5); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.min(3,100,false) +HWTEST_F_L0(BuiltinsMathTest, Min_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(100))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Min(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.pow(2,"-2") +HWTEST_F_L0(BuiltinsMathTest, Pow) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("-2"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2))); + ecmaRuntimeCallInfo->SetCallArg(1, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Pow(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.25); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.pow(-NaN,-2) +HWTEST_F_L0(BuiltinsMathTest, Pow_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Pow(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.pow() +HWTEST_F_L0(BuiltinsMathTest, Pow_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Pow(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.pow(false,-2) +HWTEST_F_L0(BuiltinsMathTest, Pow_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Pow(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.random() +HWTEST_F_L0(BuiltinsMathTest, Random) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsMath::Random(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue result2 = BuiltinsMath::Random(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + ASSERT_NE(result1.GetRawData(), result2.GetRawData()); +} + +// Math.random() +HWTEST_F_L0(BuiltinsMathTest, Random_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsMath::Random(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue result2 = BuiltinsMath::Random(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + double value1 = JSTaggedValue(static_cast(result1.GetRawData())).GetDouble(); + double value2 = JSTaggedValue(static_cast(result2.GetRawData())).GetDouble(); + ASSERT_TRUE(value1 >= 0); + ASSERT_TRUE(value1 < 1.0); + ASSERT_TRUE(value2 >= 0); + ASSERT_TRUE(value2 < 1.0); +} + +// Math.round(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Round) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Round(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.round(1.25) +HWTEST_F_L0(BuiltinsMathTest, Round_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(1.25)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Round(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.round(-0.14) +HWTEST_F_L0(BuiltinsMathTest, Round_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.14)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Round(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.round(-0.7) +HWTEST_F_L0(BuiltinsMathTest, Round_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.7)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Round(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.round(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Round_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Round(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.fround(POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Fround) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Fround(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.fround(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Fround_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Fround(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.fround(-0) +HWTEST_F_L0(BuiltinsMathTest, Fround_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Fround(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.fround(1.337) +HWTEST_F_L0(BuiltinsMathTest, Fround_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(1.337)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Fround(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.3370000123977661); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.fround(-668523145.253485) +HWTEST_F_L0(BuiltinsMathTest, Fround_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-668523145.253485)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Fround(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-668523136.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32(NaN) +HWTEST_F_L0(BuiltinsMathTest, Clz32) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(32); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32(-0) +HWTEST_F_L0(BuiltinsMathTest, Clz32_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(32); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32(1) +HWTEST_F_L0(BuiltinsMathTest, Clz32_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(31); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32(568243) +HWTEST_F_L0(BuiltinsMathTest, Clz32_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(568243))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(12); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32(4294967295) +HWTEST_F_L0(BuiltinsMathTest, Clz32_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(4294967295))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32(10000000000.123) +HWTEST_F_L0(BuiltinsMathTest, Clz32_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(10000000000.123)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(1); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32() +HWTEST_F_L0(BuiltinsMathTest, Clz32_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(32); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.hypot() +HWTEST_F_L0(BuiltinsMathTest, Hypot) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Hypot(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.hypot(-2.1) +HWTEST_F_L0(BuiltinsMathTest, Hypot_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-2.1)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Hypot(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(2.1); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.hypot(-NaN, 1) +HWTEST_F_L0(BuiltinsMathTest, Hypot_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Hypot(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + ASSERT_TRUE(result.IsDouble()); + ASSERT_TRUE(std::isnan(result.GetDouble())); +} + +// Math.hypot(true, 5, 8, -0.2, 90000) +HWTEST_F_L0(BuiltinsMathTest, Hypot_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 14); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(8))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(-0.2)); + ecmaRuntimeCallInfo->SetCallArg(4, JSTaggedValue(static_cast(90000))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Hypot(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(90000.00050022222); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Imul() +HWTEST_F_L0(BuiltinsMathTest, Imul) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Imul(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Imul("-2",9.256) +HWTEST_F_L0(BuiltinsMathTest, Imul_1) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("-2"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(9.256)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Imul(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(-18); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Imul(5,0xffffffff) +HWTEST_F_L0(BuiltinsMathTest, Imul_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0xffffffff))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Imul(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(-5); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Imul(5,0xfffffffe) +HWTEST_F_L0(BuiltinsMathTest, Imul_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0xfffffffe))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Imul(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(-10); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(-1) +HWTEST_F_L0(BuiltinsMathTest, Sin) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.8414709848078965); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(-1.5) +HWTEST_F_L0(BuiltinsMathTest, Sin_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.9974949866040544); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(null) +HWTEST_F_L0(BuiltinsMathTest, Sin_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(UNDEFINED) +HWTEST_F_L0(BuiltinsMathTest, Sin_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(true) +HWTEST_F_L0(BuiltinsMathTest, Sin_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.8414709848078965); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin("0.1") +HWTEST_F_L0(BuiltinsMathTest, Sin_6) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.09983341664682815); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(Number.POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Sin_7) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Sin_8) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(-1) +HWTEST_F_L0(BuiltinsMathTest, Sinh) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.1752011936438014); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(-1.5) +HWTEST_F_L0(BuiltinsMathTest, Sinh_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-2.1292794550948173); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(null) +HWTEST_F_L0(BuiltinsMathTest, Sinh_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(UNDEFINED) +HWTEST_F_L0(BuiltinsMathTest, Sinh_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(true) +HWTEST_F_L0(BuiltinsMathTest, Sinh_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.1752011936438014); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh("0.1") +HWTEST_F_L0(BuiltinsMathTest, Sinh_5) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.10016675001984403); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(-Number.POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Sinh_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Sinh_7) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt(-1) +HWTEST_F_L0(BuiltinsMathTest, Sqrt) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt(-0) +HWTEST_F_L0(BuiltinsMathTest, Sqrt_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt(null) +HWTEST_F_L0(BuiltinsMathTest, Sqrt_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt(true) +HWTEST_F_L0(BuiltinsMathTest, Sqrt_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt("0.1") +HWTEST_F_L0(BuiltinsMathTest, Sqrt_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.31622776601683794); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt(Number.POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Sqrt_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Sqrt_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan(-1) +HWTEST_F_L0(BuiltinsMathTest, Tan) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.5574077246549023); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan(-0) +HWTEST_F_L0(BuiltinsMathTest, Tan_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan(null) +HWTEST_F_L0(BuiltinsMathTest, Tan_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan(true) +HWTEST_F_L0(BuiltinsMathTest, Tan_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5574077246549023); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan("0.1") +HWTEST_F_L0(BuiltinsMathTest, Tan_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.10033467208545055); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan(Number.POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Tan_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Tan_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh(-1) +HWTEST_F_L0(BuiltinsMathTest, Tanh) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.7615941559557649); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh(-0) +HWTEST_F_L0(BuiltinsMathTest, Tanh_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh(null) +HWTEST_F_L0(BuiltinsMathTest, Tanh_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh(true) +HWTEST_F_L0(BuiltinsMathTest, Tanh_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.7615941559557649); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh("0.1") +HWTEST_F_L0(BuiltinsMathTest, Tanh_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.09966799462495582); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh(Number.POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Tanh_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Tanh_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc(-1) +HWTEST_F_L0(BuiltinsMathTest, Trunc) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc(-0) +HWTEST_F_L0(BuiltinsMathTest, Trunc_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc(null) +HWTEST_F_L0(BuiltinsMathTest, Trunc_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc(true) +HWTEST_F_L0(BuiltinsMathTest, Trunc_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc("-0.1") +HWTEST_F_L0(BuiltinsMathTest, Trunc_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromString("-0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc(Number.POSITIVE_INFINITY) +HWTEST_F_L0(BuiltinsMathTest, Trunc_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc(-NaN) +HWTEST_F_L0(BuiltinsMathTest, Trunc_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_number_test.cpp b/ecmascript/builtins/tests/builtins_number_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..80cd7dfac8952ce143d220a62b6bdc0715bdb9df --- /dev/null +++ b/ecmascript/builtins/tests/builtins_number_test.cpp @@ -0,0 +1,607 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "ecmascript/base/number_helper.h" +#include "ecmascript/base/string_helper.h" +#include "ecmascript/builtins/builtins_number.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_global_object.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsNumberTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +// new Number(10) +HWTEST_F_L0(BuiltinsNumberTest, NumberConstructor) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle number(env->GetNumberFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*number), 6); + ecmaRuntimeCallInfo->SetFunction(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::NumberConstructor(ecmaRuntimeCallInfo.get()); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + JSPrimitiveRef *ref = JSPrimitiveRef::Cast(value.GetTaggedObject()); + ASSERT_EQ(ref->GetValue().GetDouble(), 5.0); +} + +// Number.isFinite(-10) +HWTEST_F_L0(BuiltinsNumberTest, IsFinite) +{ + const double value = -10; + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(value))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// Number.isFinite(Number.MAX_VALUE) +HWTEST_F_L0(BuiltinsNumberTest, IsFinite1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::MAX_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// Number.isFinite("helloworld") +HWTEST_F_L0(BuiltinsNumberTest, IsFinite2) +{ + JSHandle test = thread->GetEcmaVM()->GetFactory()->NewFromString("helloworld"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Number.isFinite(NaN) +HWTEST_F_L0(BuiltinsNumberTest, IsFinite3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Number.isFinite(Infinity) +HWTEST_F_L0(BuiltinsNumberTest, IsFinite4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Number.isFinite(undefined) +HWTEST_F_L0(BuiltinsNumberTest, IsFinite5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Number.isFinite(null) +HWTEST_F_L0(BuiltinsNumberTest, IsFinite6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Number.isInteger(0.1) +HWTEST_F_L0(BuiltinsNumberTest, IsInteger) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.1)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsInteger(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Number.isNaN(0.1) +HWTEST_F_L0(BuiltinsNumberTest, IsNaN) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.1)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsNaN(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// new Number(123.456).toString(7) +HWTEST_F_L0(BuiltinsNumberTest, ToString) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberObject(env->GetNumberFunction()); + JSHandle value(thread, JSTaggedValue(123.456)); + JSHandle number = thread->GetEcmaVM()->GetFactory()->NewJSPrimitiveRef(numberObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(7.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ToString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + JSHandle correctResult = factory->NewFromString("234.312256641535441"); + CVector test(res->GetLength() + 1); + res->CopyDataUtf8(test.data(), res->GetLength()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult)); +} + +// new Number(123.456).toExponential(5) +HWTEST_F_L0(BuiltinsNumberTest, IsExponential) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberObject(env->GetNumberFunction()); + JSHandle value(thread, JSTaggedValue(123.456)); + JSHandle number = factory->NewJSPrimitiveRef(numberObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(5.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ToExponential(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + JSHandle correctResult = factory->NewFromString("1.23456e+2"); + CVector test(res->GetLength() + 1); + res->CopyDataUtf8(test.data(), res->GetLength()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult)); +} + +// new Number(123.456).toFixed(10) +HWTEST_F_L0(BuiltinsNumberTest, ToFixed) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberObject(env->GetNumberFunction()); + JSHandle value(thread, JSTaggedValue(123.456)); + JSHandle number = factory->NewJSPrimitiveRef(numberObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(10.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ToFixed(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + JSHandle correctResult = factory->NewFromString("123.4560000000"); + ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult)); +} + +// new Number(123.456).toFixed(30) +HWTEST_F_L0(BuiltinsNumberTest, ToFixed1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberObject(env->GetNumberFunction()); + JSHandle value(thread, JSTaggedValue(123.456)); + JSHandle number = factory->NewJSPrimitiveRef(numberObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(30.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ToFixed(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + JSHandle correctResult = factory->NewFromString("123.456000000000003069544618483633"); + ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult)); +} + +// new Number(1e21).toFixed(20) +HWTEST_F_L0(BuiltinsNumberTest, ToFixed2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberObject(env->GetNumberFunction()); + JSHandle value(thread, JSTaggedValue(1e21)); + JSHandle number = factory->NewJSPrimitiveRef(numberObject, value); + + auto ecmaRuntimeCallInfo = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(20.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ToFixed(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + JSHandle correctResult = factory->NewFromString("1e+21"); + CVector test(res->GetLength() + 1); + res->CopyDataUtf8(test.data(), res->GetLength()); + std::cout << test.data(); + ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult)); +} + +// new Number(123.456).toPrecision(8) +HWTEST_F_L0(BuiltinsNumberTest, ToPrecision) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberObject(env->GetNumberFunction()); + JSHandle value(thread, JSTaggedValue(123.456)); + JSHandle number = factory->NewJSPrimitiveRef(numberObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(8.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ToPrecision(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + JSHandle correctResult = factory->NewFromString("123.45600"); + ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult)); +} + +// Number.parseFloat(0x123) +HWTEST_F_L0(BuiltinsNumberTest, parseFloat) +{ + JSHandle param = thread->GetEcmaVM()->GetFactory()->NewFromString("0x123"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, param.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ParseFloat(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(0)).GetRawData()); +} + +// Number.parseFloat(0x123xx) +HWTEST_F_L0(BuiltinsNumberTest, parseFloat1) +{ + JSHandle param = thread->GetEcmaVM()->GetFactory()->NewFromString("0x123xx"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, param.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ParseFloat(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(0)).GetRawData()); +} + +// Number.parseInt(0x123) +HWTEST_F_L0(BuiltinsNumberTest, parseInt) +{ + const char *number = "0x123"; + + JSHandle param = thread->GetEcmaVM()->GetFactory()->NewFromString(number); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, param.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(16.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ParseInt(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(291)).GetRawData()); +} + +// testcases of StringToDouble flags +HWTEST_F_L0(BuiltinsNumberTest, StringToDoubleFlags) +{ + JSHandle str; + Span sp; + + // flags of IGNORE_TRAILING + + str = thread->GetEcmaVM()->GetFactory()->NewFromString("0a"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0); + str = thread->GetEcmaVM()->GetFactory()->NewFromString("0b"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0); + str = thread->GetEcmaVM()->GetFactory()->NewFromString("0o"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 00x"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 000.4_"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0.4); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 0010.s "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 10); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 0010e2"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 1000); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 0010e+3_0"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 10000); + + // flags of ALLOW_HEX + str = thread->GetEcmaVM()->GetFactory()->NewFromString("0x"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_HEX))); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 0x10 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_HEX), 16); + str = thread->GetEcmaVM()->GetFactory()->NewFromString("0x1g"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_HEX + base::IGNORE_TRAILING), 1); + str = thread->GetEcmaVM()->GetFactory()->NewFromString("0xh"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan( + base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_HEX + base::IGNORE_TRAILING))); + + // flags of ALLOW_OCTAL + str = thread->GetEcmaVM()->GetFactory()->NewFromString("0O"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_OCTAL))); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 0o10 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_OCTAL), 8); + str = thread->GetEcmaVM()->GetFactory()->NewFromString("0o1d"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_OCTAL | base::IGNORE_TRAILING), + 1); + str = thread->GetEcmaVM()->GetFactory()->NewFromString("0o8"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan( + base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_OCTAL | base::IGNORE_TRAILING))); + + // flags of ALLOW_BINARY + str = thread->GetEcmaVM()->GetFactory()->NewFromString("0b"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_BINARY))); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 0b10 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_BINARY), 2); + str = thread->GetEcmaVM()->GetFactory()->NewFromString("0b1d"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_BINARY | base::IGNORE_TRAILING), + 1); + str = thread->GetEcmaVM()->GetFactory()->NewFromString("0b2"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan( + base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_BINARY | base::IGNORE_TRAILING))); +} + +// testcases of StringToDouble radix +HWTEST_F_L0(BuiltinsNumberTest, StringToDoubleRadix) +{ + JSHandle str; + Span sp; + int radix; + + radix = 0; // default 10 + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 100); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 100.3e2 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 10030); + radix = 1; + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 0000 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 0); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 0001 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS))); + radix = 2; + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 4); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 11 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 3); + radix = 3; + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 9); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 21 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 7); + radix = 4; + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 16); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 31 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 13); + radix = 8; + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 64); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 71 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 57); + radix = 10; + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 100); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 0020 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 20); + radix = 16; + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 256); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 1e "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 30); + radix = 18; + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 324); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 1g "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 34); + radix = 25; + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 625); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 1g "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 41); + radix = 36; + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 1296); + str = thread->GetEcmaVM()->GetFactory()->NewFromString(" 1z "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 71); +} + +HWTEST_F_L0(BuiltinsNumberTest, NumberToString) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle res = factory->NewFromString("100"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(100))->Compare(*res), 0); + res = factory->NewFromString("11223344"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(11223344))->Compare(*res), 0); + res = factory->NewFromString("1234567890"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(1234567890))->Compare(*res), 0); + res = factory->NewFromString("100"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.0)))->Compare(*res), 0); + res = factory->NewFromString("100.5"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.5)))->Compare(*res), 0); + res = factory->NewFromString("100.25"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.25)))->Compare(*res), 0); + res = factory->NewFromString("100.125"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.125)))->Compare(*res), 0); + res = factory->NewFromString("100.6125"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.6125)))->Compare(*res), 0); + res = factory->NewFromString("0.0006125"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(0.0006125)))->Compare(*res), 0); + res = factory->NewFromString("-0.0006125"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(-0.0006125)))->Compare(*res), 0); + res = factory->NewFromString("-1234567890.0006125"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(-1234567890.0006125)))->Compare(*res), 0); + res = factory->NewFromString("1234567890.0006125"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(1234567890.0006125)))->Compare(*res), 0); + res = factory->NewFromString("11234567890.000612"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(11234567890.0006125)))->Compare(*res), 0); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_object_test.cpp b/ecmascript/builtins/tests/builtins_object_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85c985799ce6881e91259d033337af253d7a95c9 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_object_test.cpp @@ -0,0 +1,954 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_object.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "file_items.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsObjectTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSTaggedValue CreateBuiltinJSObject(JSThread *thread, const CString keyCStr) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle objFun = globalEnv->GetObjectFunction(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + JSHandle key(factory->NewFromString(&keyCStr[0])); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, obj, key, value); + return obj.GetTaggedValue(); +} + +JSFunction *BuiltinsObjectTestCreate(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + return globalEnv->GetObjectFunction().GetObject(); +} + +JSObject *TestNewJSObject(JSThread *thread, const JSHandle &dynClass) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSObject *obj = JSObject::Cast(factory->NewDynObject(dynClass, JSObject::MIN_PROPERTIES_LENGTH)); + + obj->SetElements(thread, factory->EmptyArray().GetTaggedValue(), SKIP_BARRIER); + return obj; +} + +// 19.1.1.1 Object ( [ value ] ) +HWTEST_F_L0(BuiltinsObjectTest, ObjectConstructor) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle objectFunc(thread, BuiltinsObjectTestCreate(thread)); + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle objFun(globalEnv->GetObjectFunction()); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::ObjectConstructor(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle jtHandle(thread, JSTaggedValue(reinterpret_cast(result.GetRawData()))); + JSTaggedValue resultProto = jtHandle->GetPrototype(thread); + JSTaggedValue funcProto = objectFunc->GetFunctionPrototype(); + ASSERT_EQ(resultProto, funcProto); + ASSERT_TRUE(jtHandle->IsExtensible()); + + // num_args = 0 + JSHandle object = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(function), function); + object->GetJSHClass()->SetCallable(true); + auto tgObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*object), 4); + tgObjCallInfo->SetFunction(JSTaggedValue(*objFun)); + tgObjCallInfo->SetThis(JSTaggedValue::Undefined()); + tgObjCallInfo->SetNewTarget(JSTaggedValue(*objFun)); + + prev = TestHelper::SetupFrame(thread, tgObjCallInfo.get()); + JSTaggedValue resultTg = BuiltinsObject::ObjectConstructor(tgObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(resultTg.IsObject()); + JSHandle jtHandleTg(thread, JSTaggedValue(reinterpret_cast(resultTg.GetRawData()))); + JSTaggedValue resultProtoTg = jtHandleTg->GetPrototype(thread); + JSTaggedValue funcProtoTg = objectFunc->GetFunctionPrototype(); + ASSERT_EQ(resultProtoTg, funcProtoTg); + ASSERT_TRUE(jtHandleTg->IsExtensible()); + + // value is null + JSHandle objectVn = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(function), function); + objectVn->GetJSHClass()->SetCallable(true); + auto vnObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*objectVn), 6); + vnObjCallInfo->SetFunction(JSTaggedValue(*objFun)); + vnObjCallInfo->SetThis(JSTaggedValue::Undefined()); + vnObjCallInfo->SetCallArg(0, JSTaggedValue::Null()); + vnObjCallInfo->SetNewTarget(JSTaggedValue(*objFun)); + + prev = TestHelper::SetupFrame(thread, vnObjCallInfo.get()); + JSTaggedValue resultVn = BuiltinsObject::ObjectConstructor(vnObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultVn.IsObject()); + JSHandle jtHandleVn(thread, JSTaggedValue(reinterpret_cast(resultVn.GetRawData()))); + JSTaggedValue resultProtoVn = jtHandleVn->GetPrototype(thread); + JSTaggedValue funcProtoVn = objectFunc->GetFunctionPrototype(); + ASSERT_EQ(resultProtoVn, funcProtoVn); + ASSERT_TRUE(jtHandleVn->IsExtensible()); +} + +// 19.1.2.1 Object.assign ( target, ...sources ) +HWTEST_F_L0(BuiltinsObjectTest, Assign) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle objHandle1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + JSHandle objHandle2 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString("x")); + JSHandle value1(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(objHandle1), key1, value1); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(objHandle1), key1).GetValue()->GetInt(), 1); + + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromString("y")); + JSHandle value2(thread, JSTaggedValue(2)); + JSObject::SetProperty(thread, JSHandle(objHandle2), key2, value2); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(objHandle2), key2).GetValue()->GetInt(), 2); + + auto assignObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + assignObjCallInfo->SetFunction(JSTaggedValue::Undefined()); + assignObjCallInfo->SetThis(JSTaggedValue::Undefined()); + assignObjCallInfo->SetCallArg(0, objHandle1.GetTaggedValue()); + assignObjCallInfo->SetCallArg(1, objHandle2.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, assignObjCallInfo.get()); + JSTaggedValue result = BuiltinsObject::Assign(assignObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle jtHandle(thread, JSTaggedValue(reinterpret_cast(result.GetRawData()))); + EXPECT_EQ(JSObject::GetProperty(thread, jtHandle, key1).GetValue()->GetInt(), 1); + EXPECT_EQ(JSObject::GetProperty(thread, jtHandle, key2).GetValue()->GetInt(), 2); +} + +// 19.1.2.2 Object.create ( O [ , Properties ] ) +HWTEST_F_L0(BuiltinsObjectTest, Create) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle objectFunc(thread, BuiltinsObjectTestCreate(thread)); + JSHandle funcProto(thread, objectFunc->GetFunctionPrototype()); + + // no prop + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, funcProto.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::Create(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle jtHandle(thread, JSTaggedValue(reinterpret_cast(result.GetRawData()))); + JSTaggedValue resultProto = jtHandle->GetPrototype(thread); + ASSERT_EQ(resultProto, funcProto.GetTaggedValue()); + + // has prop + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("prop")); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(function), function); + EXPECT_TRUE(*objHandle != nullptr); + + PropertyDescriptor desc(thread); + desc.SetWritable(false); + JSHandle descHandle(JSObject::FromPropertyDescriptor(thread, desc)); + + PropertyDescriptor descNw(thread, JSHandle::Cast(descHandle), true, true, true); + JSObject::DefineOwnProperty(thread, objHandle, key, descNw); + + auto hpObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + hpObjCallInfo->SetFunction(JSTaggedValue::Undefined()); + hpObjCallInfo->SetThis(JSTaggedValue::Undefined()); + hpObjCallInfo->SetCallArg(0, funcProto.GetTaggedValue()); + hpObjCallInfo->SetCallArg(1, objHandle.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, hpObjCallInfo.get()); + JSTaggedValue resultHp = BuiltinsObject::Create(hpObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultHp.IsObject()); + PropertyDescriptor descRes(thread); + bool success = JSObject::GetOwnProperty(thread, JSHandle(thread, resultHp), key, descRes); + EXPECT_TRUE(success); + EXPECT_TRUE(!descRes.IsWritable()); + + // undefined + auto unCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + unCallInfo->SetFunction(JSTaggedValue::Undefined()); + unCallInfo->SetThis(JSTaggedValue::Undefined()); + unCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + prev = TestHelper::SetupFrame(thread, unCallInfo.get()); + JSTaggedValue resultUn = BuiltinsObject::Create(unCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(resultUn.GetRawData(), JSTaggedValue::VALUE_EXCEPTION); +} + +// 19.1.2.3 Object.defineProperties ( O, Properties ) +HWTEST_F_L0(BuiltinsObjectTest, DefineProperties) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle objFunc(env->GetObjectFunction()); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("prop")); + JSHandle jsobjHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + PropertyDescriptor desc(thread); + desc.SetWritable(false); + JSHandle descHandle(JSObject::FromPropertyDescriptor(thread, desc)); + + PropertyDescriptor descNw(thread, JSHandle::Cast(descHandle), true, true, true); + JSObject::DefineOwnProperty(thread, jsobjHandle, key, descNw); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, objHandle.GetTaggedValue()); + objCallInfo->SetCallArg(1, jsobjHandle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::DefineProperties(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + JSTaggedValue res(reinterpret_cast(result.GetRawData())); + PropertyDescriptor descRes(thread); + JSObject::GetOwnProperty(thread, JSHandle(thread, res), key, descRes); + EXPECT_TRUE(!descRes.IsWritable()); +} + +// 19.1.2.4 Object.defineProperty ( O, P, Attributes ) +HWTEST_F_L0(BuiltinsObjectTest, DefineProperty) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle objFunc(env->GetObjectFunction()); + JSHandle attHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + PropertyDescriptor desc(thread); + desc.SetWritable(true); + JSHandle writableStr = thread->GlobalConstants()->GetHandledWritableString(); + JSHandle writable(thread, JSTaggedValue(desc.IsWritable())); + JSObject::CreateDataProperty(thread, attHandle, writableStr, writable); + + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("x")); + + PropertyDescriptor descNw(thread); + JSObject::GetOwnProperty(thread, objHandle, key, descNw); + EXPECT_TRUE(!descNw.HasWritable()); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, objHandle.GetTaggedValue()); + objCallInfo->SetCallArg(1, key.GetTaggedValue()); + objCallInfo->SetCallArg(2, attHandle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::DefineProperty(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + JSTaggedValue res(reinterpret_cast(result.GetRawData())); + PropertyDescriptor descRes(thread); + JSObject::GetOwnProperty(thread, JSHandle(thread, res), key, descRes); + EXPECT_TRUE(descRes.HasWritable()); +} + +// 19.1.2.5 Object.freeze ( O ) +HWTEST_F_L0(BuiltinsObjectTest, Freeze) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + // An object is extensible by default, so it is also non-frozen. + JSHandle emptyObj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + emptyObj->GetJSHClass()->SetExtensible(true); + auto nofreezeObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + nofreezeObjCallInfo->SetFunction(JSTaggedValue::Undefined()); + nofreezeObjCallInfo->SetThis(JSTaggedValue::Undefined()); + nofreezeObjCallInfo->SetCallArg(0, emptyObj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, nofreezeObjCallInfo.get()); + JSTaggedValue result = BuiltinsObject::IsFrozen(nofreezeObjCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); + + BuiltinsObject::Freeze(nofreezeObjCallInfo.get()); + JSTaggedValue resultIs = BuiltinsObject::IsFrozen(nofreezeObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(resultIs.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// 19.1.2.6 Object.getOwnPropertyDescriptor ( O, P ) +HWTEST_F_L0(BuiltinsObjectTest, GetOwnPropertyDesciptor) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("x")); + + PropertyDescriptor descEnum(thread); + descEnum.SetWritable(true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(objHandle), key, descEnum); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, objHandle.GetTaggedValue()); + objCallInfo->SetCallArg(1, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::GetOwnPropertyDesciptor(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + + JSHandle writableStr = thread->GlobalConstants()->GetHandledWritableString(); + JSTaggedValue jt(reinterpret_cast(result.GetRawData())); + PropertyDescriptor desc(thread); + JSObject::GetOwnProperty(thread, JSHandle(thread, jt), writableStr, desc); + ASSERT_TRUE(JSTaggedValue::SameValue(desc.GetValue().GetTaggedValue(), JSTaggedValue(true))); +} + +// 19.1.2.7 Object.getOwnPropertyNames ( O ) +HWTEST_F_L0(BuiltinsObjectTest, GetOwnPropertyNames) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(objHandle), key, value); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, objHandle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::GetOwnPropertyNames(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); +} + +// 19.1.2.8 Object.getOwnPropertySymbols ( O ) +HWTEST_F_L0(BuiltinsObjectTest, GetOwnPropertySymbols) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + JSHandle symbolKey = thread->GetEcmaVM()->GetFactory()->NewJSSymbol(); + JSHandle key(symbolKey); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(objHandle), key, value); + thread->ClearException(); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, objHandle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::GetOwnPropertySymbols(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); +} + +// 19.1.2.10 Object.is ( value1, value2 ) +HWTEST_F_L0(BuiltinsObjectTest, Is) +{ + // js object compare + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + + JSHandle obj1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + JSHandle obj2 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + auto objCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + objCallInfo1->SetFunction(JSTaggedValue::Undefined()); + objCallInfo1->SetThis(JSTaggedValue::Undefined()); + objCallInfo1->SetCallArg(0, obj1.GetTaggedValue()); + objCallInfo1->SetCallArg(1, obj2.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo1.get()); + JSTaggedValue objResult1 = BuiltinsObject::Is(objCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(objResult1.GetRawData(), JSTaggedValue::False().GetRawData()); + + objCallInfo1->SetCallArg(1, obj1.GetTaggedValue()); + JSTaggedValue objResult2 = BuiltinsObject::Is(objCallInfo1.get()); + ASSERT_EQ(objResult2.GetRawData(), JSTaggedValue::True().GetRawData()); + + // string compare + JSHandle testStrValue1 = thread->GetEcmaVM()->GetFactory()->NewFromString("helloworld"); + JSHandle testStrValue2 = thread->GetEcmaVM()->GetFactory()->NewFromString("helloworld"); + + auto strCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + strCallInfo->SetFunction(JSTaggedValue::Undefined()); + strCallInfo->SetThis(JSTaggedValue::Undefined()); + strCallInfo->SetCallArg(0, testStrValue1.GetTaggedValue()); + strCallInfo->SetCallArg(1, testStrValue2.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, strCallInfo.get()); + JSTaggedValue strResult = BuiltinsObject::Is(strCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(strResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + // bool compare + auto boolCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + boolCallInfo->SetFunction(JSTaggedValue::Undefined()); + boolCallInfo->SetThis(JSTaggedValue::Undefined()); + boolCallInfo->SetCallArg(0, JSTaggedValue::True()); + boolCallInfo->SetCallArg(1, JSTaggedValue::False()); + + prev = TestHelper::SetupFrame(thread, boolCallInfo.get()); + JSTaggedValue boolResult = BuiltinsObject::Is(boolCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(boolResult.GetRawData(), JSTaggedValue::False().GetRawData()); + + // number compare + auto numCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + numCallInfo->SetFunction(JSTaggedValue::Undefined()); + numCallInfo->SetThis(JSTaggedValue::Undefined()); + numCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + numCallInfo->SetCallArg(1, JSTaggedValue(-0.0)); + + prev = TestHelper::SetupFrame(thread, numCallInfo.get()); + JSTaggedValue numResult = BuiltinsObject::Is(numCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(numResult.GetRawData(), JSTaggedValue::False().GetRawData()); + + // undefined or null compare + auto nullCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + nullCallInfo->SetFunction(JSTaggedValue::Undefined()); + nullCallInfo->SetThis(JSTaggedValue::Undefined()); + nullCallInfo->SetCallArg(0, JSTaggedValue::Null()); + nullCallInfo->SetCallArg(1, JSTaggedValue::Null()); + + prev = TestHelper::SetupFrame(thread, nullCallInfo.get()); + JSTaggedValue nullResult = BuiltinsObject::Is(nullCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(nullResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + auto undefineCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + undefineCallInfo->SetFunction(JSTaggedValue::Undefined()); + undefineCallInfo->SetThis(JSTaggedValue::Undefined()); + undefineCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + undefineCallInfo->SetCallArg(1, JSTaggedValue::Undefined()); + + prev = TestHelper::SetupFrame(thread, undefineCallInfo.get()); + JSTaggedValue undefineResult = BuiltinsObject::Is(undefineCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(undefineResult.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// 19.1.2.11 Object.isExtensible ( O ) +HWTEST_F_L0(BuiltinsObjectTest, IsExtensible) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + + // New objects can be extended by default. + JSHandle emptyObj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + emptyObj->GetJSHClass()->SetExtensible(true); + auto emptyObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + emptyObjCallInfo->SetFunction(JSTaggedValue::Undefined()); + emptyObjCallInfo->SetThis(JSTaggedValue::Undefined()); + emptyObjCallInfo->SetCallArg(0, emptyObj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, emptyObjCallInfo.get()); + JSTaggedValue result = BuiltinsObject::IsExtensible(emptyObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + + emptyObj->GetJSHClass()->SetExtensible(false); + JSTaggedValue result2 = BuiltinsObject::IsExtensible(emptyObjCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// 19.1.2.12 Object.isFrozen ( O ) +HWTEST_F_L0(BuiltinsObjectTest, IsFrozen) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + + // An object is extensible by default, so it is also non-frozen. + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(function), function); + obj->GetJSHClass()->SetExtensible(true); + auto emptyObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + emptyObjCallInfo->SetFunction(JSTaggedValue::Undefined()); + emptyObjCallInfo->SetThis(JSTaggedValue::Undefined()); + emptyObjCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, emptyObjCallInfo.get()); + JSTaggedValue result = BuiltinsObject::IsFrozen(emptyObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); + + obj->GetJSHClass()->SetExtensible(false); + prev = TestHelper::SetupFrame(thread, emptyObjCallInfo.get()); + JSTaggedValue resultNex = BuiltinsObject::IsFrozen(emptyObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(resultNex.GetRawData(), JSTaggedValue::True().GetRawData()); + + PropertyDescriptor descEnum(thread); + descEnum.SetConfigurable(true); + descEnum.SetWritable(false); + obj->GetJSHClass()->SetExtensible(true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, descEnum); + obj->GetJSHClass()->SetExtensible(false); + auto emptyObjCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + emptyObjCallInfo2->SetFunction(JSTaggedValue::Undefined()); + emptyObjCallInfo2->SetThis(JSTaggedValue::Undefined()); + emptyObjCallInfo2->SetCallArg(0, obj.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, emptyObjCallInfo2.get()); + JSTaggedValue resultNw = BuiltinsObject::IsFrozen(emptyObjCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(resultNw.GetRawData(), JSTaggedValue::False().GetRawData()); + + descEnum.SetConfigurable(false); + obj->GetJSHClass()->SetExtensible(true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, descEnum); + obj->GetJSHClass()->SetExtensible(false); + auto emptyObjCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + emptyObjCallInfo3->SetFunction(JSTaggedValue::Undefined()); + emptyObjCallInfo3->SetThis(JSTaggedValue::Undefined()); + emptyObjCallInfo3->SetCallArg(0, obj.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, emptyObjCallInfo3.get()); + JSTaggedValue resultNc = BuiltinsObject::IsFrozen(emptyObjCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(resultNc.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// 19.1.2.13 Object.isSealed ( O ) +HWTEST_F_L0(BuiltinsObjectTest, IsSealed) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + + // An object is extensible by default, so it is also non-frozen. + JSHandle emptyObj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + emptyObj->GetJSHClass()->SetExtensible(true); + auto emptyObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + emptyObjCallInfo->SetFunction(JSTaggedValue::Undefined()); + emptyObjCallInfo->SetThis(JSTaggedValue::Undefined()); + emptyObjCallInfo->SetCallArg(0, emptyObj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, emptyObjCallInfo.get()); + JSTaggedValue result = BuiltinsObject::IsSealed(emptyObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Object.keys(obj) +HWTEST_F_L0(BuiltinsObjectTest, Keys) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::Keys(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); +} + +// Object.preventExtensions(obj) +HWTEST_F_L0(BuiltinsObjectTest, PreventExtensions) +{ + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject(thread, "x")); + obj->GetJSHClass()->SetExtensible(true); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::PreventExtensions(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + JSTaggedValue jt(reinterpret_cast(result.GetRawData())); + JSHandle jtHandle(thread, jt); + ASSERT_TRUE(!jtHandle->IsExtensible()); +} + +// Object.seal(obj) +HWTEST_F_L0(BuiltinsObjectTest, Seal) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::Seal(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + ASSERT_EQ(result.GetRawData(), obj->GetRawData()); + + // test isSealed(). + JSTaggedValue res = BuiltinsObject::IsSealed(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(res.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// Object.setPrototypeOf(obj, prototype) +HWTEST_F_L0(BuiltinsObjectTest, SetPrototypeOf) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + JSHandle objFather(thread, CreateBuiltinJSObject(thread, "y")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, objFather.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::SetPrototypeOf(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + ASSERT_EQ(result.GetRawData(), obj.GetTaggedValue().GetRawData()); + + // test obj has property "y". + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("y")); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); +} + +// obj.hasOwnProperty(prop) +HWTEST_F_L0(BuiltinsObjectTest, HasOwnProperty) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + CString keyCStr = "x"; + JSHandle keyString = thread->GetEcmaVM()->GetFactory()->NewFromString(&keyCStr[0]); + JSHandle key(keyString); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::HasOwnProperty(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// prototypeObj.isPrototypeOf(object) +HWTEST_F_L0(BuiltinsObjectTest, IsPrototypeOfFalse) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + JSHandle objFather(thread, CreateBuiltinJSObject(thread, "y")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(objFather.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsObject::IsPrototypeOf(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +HWTEST_F_L0(BuiltinsObjectTest, IsPrototypeOfTrue) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + JSHandle objFather(thread, CreateBuiltinJSObject(thread, "y")); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, objFather.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsObject::SetPrototypeOf(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result1.IsObject()); + ASSERT_EQ(result1.GetRawData(), obj->GetRawData()); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(objFather.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, obj.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsObject::IsPrototypeOf(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// obj.propertyIsEnumerable(prop) +HWTEST_F_L0(BuiltinsObjectTest, PropertyIsEnumerable) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("x")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::PropertyIsEnumerable(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// obj.toLocaleString() +HWTEST_F_L0(BuiltinsObjectTest, ToLocaleString) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + JSHandle calleeFunc = thread->GetEcmaVM()->GetFactory()->NewJSFunction( + thread->GetEcmaVM()->GetGlobalEnv(), reinterpret_cast(BuiltinsObject::ToString)); + calleeFunc->GetClass()->SetCallable(true); + JSHandle calleeValue(calleeFunc); + JSHandle calleeKey(thread->GetEcmaVM()->GetFactory()->NewFromString("toString")); + JSObject::SetProperty(thread, obj, calleeKey, calleeValue); + + JSHandle resultValue = thread->GetEcmaVM()->GetFactory()->NewFromString("[object Object]"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::ToLocaleString(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsString()); + ASSERT_EQ(resultValue->Compare(reinterpret_cast(result.GetRawData())), 0); +} + +// obj.toString() +HWTEST_F_L0(BuiltinsObjectTest, ToString) +{ + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject(thread, "x")); + + // object + JSHandle resultValue = thread->GetEcmaVM()->GetFactory()->NewFromString("[object Object]"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::ToString(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsString()); + ASSERT_EQ(resultValue->Compare(reinterpret_cast(result.GetRawData())), 0); + + // array + JSHandle arr = thread->GetEcmaVM()->GetFactory()->NewJSArray(); + JSHandle resultArrValue = thread->GetEcmaVM()->GetFactory()->NewFromString("[object Array]"); + auto arrEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + arrEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + arrEcmaRuntimeCallInfo->SetThis(arr.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, arrEcmaRuntimeCallInfo.get()); + JSTaggedValue resultArr = BuiltinsObject::ToString(arrEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultArr.IsString()); + ASSERT_EQ(resultArrValue->Compare(reinterpret_cast(resultArr.GetRawData())), 0); + + // string + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromString("hello"); + JSHandle resultStrValue = thread->GetEcmaVM()->GetFactory()->NewFromString("[object String]"); + auto strEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + strEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + strEcmaRuntimeCallInfo->SetThis(str.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, strEcmaRuntimeCallInfo.get()); + JSTaggedValue resultStr = BuiltinsObject::ToString(strEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultStr.IsString()); + ASSERT_EQ(resultStrValue->Compare(reinterpret_cast(resultStr.GetRawData())), 0); + + // function + JSHandle func = thread->GetEcmaVM()->GetFactory()->NewJSFunction(thread->GetEcmaVM()->GetGlobalEnv()); + JSHandle resultFuncValue = thread->GetEcmaVM()->GetFactory()->NewFromString("[object Function]"); + auto funcEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + funcEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + funcEcmaRuntimeCallInfo->SetThis(func.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, funcEcmaRuntimeCallInfo.get()); + JSTaggedValue resultFunc = BuiltinsObject::ToString(funcEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultFunc.IsString()); + ASSERT_EQ(resultFuncValue->Compare(reinterpret_cast(resultFunc.GetRawData())), 0); + + // error + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle errorObject = env->GetErrorFunction(); + JSHandle error = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + JSHandle errorValue = thread->GetEcmaVM()->GetFactory()->NewFromString("[object Error]"); + auto errorEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + errorEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + errorEcmaRuntimeCallInfo->SetThis(error.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, errorEcmaRuntimeCallInfo.get()); + JSTaggedValue resultError = BuiltinsObject::ToString(errorEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultError.IsString()); + ASSERT_EQ(errorValue->Compare(reinterpret_cast(resultError.GetRawData())), 0); + + // boolean + JSHandle value(thread, JSTaggedValue::False()); + JSHandle booleanObject(env->GetBooleanFunction()); + JSHandle boolean = thread->GetEcmaVM()->GetFactory()->NewJSPrimitiveRef(booleanObject, value); + JSHandle resultBoolValue = thread->GetEcmaVM()->GetFactory()->NewFromString("[object Boolean]"); + auto boolEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + boolEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + boolEcmaRuntimeCallInfo->SetThis(boolean.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, boolEcmaRuntimeCallInfo.get()); + JSTaggedValue resultBool = BuiltinsObject::ToString(boolEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultBool.IsString()); + ASSERT_EQ(resultBoolValue->Compare(reinterpret_cast(resultBool.GetRawData())), 0); + + // number + JSHandle resultNumValue = thread->GetEcmaVM()->GetFactory()->NewFromString("[object Number]"); + auto numEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + numEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + numEcmaRuntimeCallInfo->SetThis(JSTaggedValue(static_cast(0))); + + prev = TestHelper::SetupFrame(thread, numEcmaRuntimeCallInfo.get()); + JSTaggedValue resultNum = BuiltinsObject::ToString(numEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultNum.IsString()); + ASSERT_EQ(resultNumValue->Compare(reinterpret_cast(resultNum.GetRawData())), 0); +} + +// object.valueOf() +HWTEST_F_L0(BuiltinsObjectTest, ValueOf) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::ValueOf(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_promise_test.cpp b/ecmascript/builtins/tests/builtins_promise_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cf31f16c11cadbcc25251904afd0708d483b105d --- /dev/null +++ b/ecmascript/builtins/tests/builtins_promise_test.cpp @@ -0,0 +1,725 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_promise.h" +#include "ecmascript/builtins/builtins_array.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/jobs/micro_job_queue.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using BuiltinsBase = panda::ecmascript::base::BuiltinsBase; +using JSArray = panda::ecmascript::JSArray; + +class BuiltinsPromiseTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +// native function for race2 then_on_rejected() +JSTaggedValue TestPromiseRaceThenOnRejectd(EcmaRuntimeCallInfo *argv) +{ + JSHandle result = BuiltinsBase::GetCallArg(argv, 0); + // 12345 : test case + EXPECT_EQ(JSTaggedValue::SameValue(result.GetTaggedValue(), JSTaggedValue(12345)), true); + return JSTaggedValue::Undefined(); +} + +// native function for all then_on_resolved() +JSTaggedValue TestPromiseAllThenOnResolved(EcmaRuntimeCallInfo *argv) +{ + JSHandle array = BuiltinsBase::GetCallArg(argv, 0); + JSHandle objectArray = JSHandle::Cast(array); + [[maybe_unused]] PropertyDescriptor desc(argv->GetThread()); + [[maybe_unused]] bool result1 = JSObject::GetOwnProperty( + argv->GetThread(), objectArray, JSHandle(argv->GetThread(), JSTaggedValue(0)), desc); + EXPECT_TRUE(result1); + JSHandle value1 = desc.GetValue(); + // 111 : test case + EXPECT_EQ(JSTaggedValue::SameValue(value1.GetTaggedValue(), JSTaggedValue(111)), true); + [[maybe_unused]] bool result2 = JSObject::GetOwnProperty( + argv->GetThread(), objectArray, JSHandle(argv->GetThread(), JSTaggedValue(1)), desc); + EXPECT_TRUE(result2); + JSHandle value2 = desc.GetValue(); + // 222 : test case + EXPECT_EQ(JSTaggedValue::SameValue(value2.GetTaggedValue(), JSTaggedValue(222)), true); + return JSTaggedValue::Undefined(); +} + +// native function for catch catch_on_rejected() +JSTaggedValue TestPromiseCatch(EcmaRuntimeCallInfo *argv) +{ + JSHandle result = BuiltinsBase::GetCallArg(argv, 0); + // 3 : test case + EXPECT_EQ(JSTaggedValue::SameValue(result.GetTaggedValue(), JSTaggedValue(3)), true); + return JSTaggedValue::Undefined(); +} + +// native function for then then_on_resolved() +JSTaggedValue TestPromiseThenOnResolved(EcmaRuntimeCallInfo *argv) +{ + auto factory = argv->GetThread()->GetEcmaVM()->GetFactory(); + JSHandle result = BuiltinsBase::GetCallArg(argv, 0); + auto expect = factory->NewFromString("resolve"); + EXPECT_EQ(JSTaggedValue::SameValue(result.GetTaggedValue(), expect.GetTaggedValue()), true); + return JSTaggedValue::Undefined(); +} + +// native function for then then_on_rejected() +JSTaggedValue TestPromiseThenOnRejected(EcmaRuntimeCallInfo *argv) +{ + auto factory = argv->GetThread()->GetEcmaVM()->GetFactory(); + JSHandle result = BuiltinsBase::GetCallArg(argv, 0); + auto expect = factory->NewFromString("reject"); + EXPECT_EQ(JSTaggedValue::SameValue(result.GetTaggedValue(), expect.GetTaggedValue()), true); + return JSTaggedValue::Undefined(); +} + +/* + * @tc.name: Reject1 + * @tc.desc: The reject method receives a number. + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsPromiseTest, Reject1) +{ + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg(thread, JSTaggedValue(3)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg.GetTaggedValue()); + + /** + * @tc.steps: var p1 = Promise.reject(3). + */ + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = BuiltinsPromise::Reject(ecmaRuntimeCallInfo1.get()); + JSHandle rejectPromise(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), JSTaggedValue(3)), true); +} + +/* + * @tc.name: Reject2 + * @tc.desc: The reject method receives a promise object. + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsPromiseTest, Reject2) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + // constructor promise1 + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg1 = JSHandle::Cast(factory->NewFromString("Promise reject")); + + /** + * @tc.steps: step1. var p1 = Promise.reject("Promise reject") + */ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, promise.GetTaggedValue(), 6); + ecmaRuntimeCallInfo->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg1.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsPromise::Reject(ecmaRuntimeCallInfo.get()); + JSHandle promise1(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(promise1->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(promise1->GetPromiseResult(), paramMsg1.GetTaggedValue()), true); + + /** + * @tc.steps: step2. var p2 = Promise.reject(p1) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, promise.GetTaggedValue(), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, promise1.GetTaggedValue()); + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + JSTaggedValue result1 = BuiltinsPromise::Reject(ecmaRuntimeCallInfo1.get()); + JSHandle promise2(thread, result1); + EXPECT_NE(*promise1, *promise2); + EXPECT_EQ(JSTaggedValue::SameValue(promise2->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ( + JSTaggedValue::SameValue(promise2->GetPromiseResult(), JSTaggedValue(promise1.GetTaggedValue().GetRawData())), + true); +} + +/* + * @tc.name: Resolve1 + * @tc.desc: The resolve method receives a number. + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsPromiseTest, Resolve1) +{ + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg(thread, JSTaggedValue(5)); + + /** + * @tc.steps: step1. var p1 = Promise.resolve(12345) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo1.get()); + JSHandle rejectPromise(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), JSTaggedValue(5)), true); +} + +/* + * @tc.name: Resolve2 + * @tc.desc: The resolve method receives a promise object. + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsPromiseTest, Resolve2) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + // constructor promise1 + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg1 = JSHandle::Cast(factory->NewFromString("Promise reject")); + + /** + * @tc.steps: step1. var p1 = Promise.reject("Promise reject") + */ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, promise.GetTaggedValue(), 6); + ecmaRuntimeCallInfo->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg1.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsPromise::Reject(ecmaRuntimeCallInfo.get()); + JSHandle promise1(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(promise1->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(promise1->GetPromiseResult(), paramMsg1.GetTaggedValue()), true); + + // promise1 Enter Reject() as a parameter. + /** + * @tc.steps: step2. var p2 = Promise.resolve(p1) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, promise.GetTaggedValue(), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, promise1.GetTaggedValue()); + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + JSTaggedValue result1 = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo1.get()); + JSHandle promise2(thread, result1); + EXPECT_EQ(*promise1, *promise2); + EXPECT_EQ(JSTaggedValue::SameValue(promise2->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(promise2->GetPromiseResult(), paramMsg1.GetTaggedValue()), true); +} + +/* + * @tc.name: Race1 + * @tc.desc: The race method receives an array. + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsPromiseTest, Race1) +{ + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg1(thread, JSTaggedValue(12345)); + JSHandle paramMsg2(thread, JSTaggedValue(6789)); + + /** + * @tc.steps: step1. var p1 = Promise.reject(12345) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg1.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsPromise::Reject(ecmaRuntimeCallInfo1.get()); + JSHandle rejectPromise(thread, result1); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), JSTaggedValue(12345)), true); + + /** + * @tc.steps: step2. var p2 = Promise.resolve(6789) + */ + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo2->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, paramMsg2.GetTaggedValue()); + + [[maybe_unused]] auto prevResolve = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo2.get()); + JSHandle resolvePromise(thread, result2); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseResult(), JSTaggedValue(6789)), true); + + /** + * @tc.steps: step3. Construct an array with two elements p1 and p2. array = [p1. p2] + */ + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(2))); + PropertyDescriptor desc(thread, JSHandle::Cast(rejectPromise)); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(0)), desc); + + PropertyDescriptor desc1(thread, JSHandle::Cast(resolvePromise)); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(1)), desc1); + + /** + * @tc.steps: step4. var p3 = Promise.race([p1,p2]); + */ + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo4->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, array.GetTaggedValue()); + + [[maybe_unused]] auto prev4 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue result4 = BuiltinsPromise::Race(ecmaRuntimeCallInfo4.get()); + JSHandle racePromise(thread, result4); + EXPECT_EQ(JSTaggedValue::SameValue(racePromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(racePromise->GetPromiseResult().IsUndefined(), true); +} + +/** + * @tc.name: Race2 + * @tc.desc: The Race method receives an array, uses the Then method to save the task in the task queue, and outputs + * the execution result of the queue. + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsPromiseTest, Race2) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg1(thread, JSTaggedValue(12345)); + JSHandle paramMsg2(thread, JSTaggedValue(6789)); + + /** + * @tc.steps: step1. var p1 = Promise.reject(12345) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg1.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsPromise::Reject(ecmaRuntimeCallInfo1.get()); + JSHandle rejectPromise(thread, result1); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), JSTaggedValue(12345)), true); + + /** + * @tc.steps: step2. var p2 = Promise.resolve(6789) + */ + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo2->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, paramMsg2.GetTaggedValue()); + + [[maybe_unused]] auto prevResolve = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo2.get()); + JSHandle resolvePromise(thread, result2); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseResult(), JSTaggedValue(6789)), true); + + /** + * @tc.steps: step3. Construct an array with two elements p1 and p2. array = [p1. p2] + */ + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(2))); + PropertyDescriptor desc(thread, JSHandle::Cast(rejectPromise)); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(0)), desc); + + PropertyDescriptor desc1(thread, JSHandle::Cast(resolvePromise)); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(1)), desc1); + + /** + * @tc.steps: step4. var p3 = Promise.race([p1,p2]); + */ + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo4->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, array.GetTaggedValue()); + + [[maybe_unused]] auto prev4 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue result4 = BuiltinsPromise::Race(ecmaRuntimeCallInfo4.get()); + JSHandle racePromise(thread, result4); + EXPECT_EQ(JSTaggedValue::SameValue(racePromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(racePromise->GetPromiseResult().IsUndefined(), true); + + /** + * @tc.steps: step5. p3.then((resolve)=>{print(resolve)}, (reject)=>{print(reject)}) + */ + JSHandle native_func_race_then_onrejected = + factory->NewJSFunction(env, reinterpret_cast(TestPromiseRaceThenOnRejectd)); + auto ecmaRuntimeCallInfo5 = TestHelper::CreateEcmaRuntimeCallInfo(thread, racePromise.GetTaggedValue(), 8); + ecmaRuntimeCallInfo5->SetFunction(racePromise.GetTaggedValue()); + ecmaRuntimeCallInfo5->SetThis(racePromise.GetTaggedValue()); + ecmaRuntimeCallInfo5->SetCallArg(0, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo5->SetCallArg(1, native_func_race_then_onrejected.GetTaggedValue()); + + [[maybe_unused]] auto prev5 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue thenResult = BuiltinsPromise::Then(ecmaRuntimeCallInfo5.get()); + JSHandle thenPromise(thread, thenResult); + + EXPECT_TRUE(JSTaggedValue::SameValue(thenPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING)))); + EXPECT_TRUE(thenPromise->GetPromiseResult().IsUndefined()); + + /** + * @tc.steps: step6. execute promise queue + */ + auto microJobQueue = EcmaVM::Cast(instance)->GetMicroJobQueue(); + if (!thread->HasPendingException()) { + microJobQueue->ExecutePendingJob(thread); + } +} + +/* + * @tc.name: All + * @tc.desc: The All method receives an array, uses the Then method to save the task in the task queue, and outputs the + * execution result of the queue. + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsPromiseTest, All) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg1(thread, JSTaggedValue(111)); + JSHandle paramMsg2(thread, JSTaggedValue(222)); + + /** + * @tc.steps: step1. var p1 = Promise.resolve(111) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg1.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo1.get()); + JSHandle resolvePromise1(thread, result1); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise1->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise1->GetPromiseResult(), JSTaggedValue(111)), true); + + /** + * @tc.steps: step2. var p2 = Promise.resolve(222) + */ + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo2->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, paramMsg2.GetTaggedValue()); + + [[maybe_unused]] auto prevResolve = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo2.get()); + JSHandle resolvePromise2(thread, result2); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise2->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise2->GetPromiseResult(), JSTaggedValue(222)), true); + + /** + * @tc.steps: step3. Construct an array with two elements p1 and p2. array = [p1. p2] + */ + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(2))); + PropertyDescriptor desc(thread, JSHandle::Cast(resolvePromise1)); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(0)), desc); + + PropertyDescriptor desc1(thread, JSHandle::Cast(resolvePromise2)); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(1)), desc1); + + /** + * @tc.steps: step4. var p3 = Promise.all([p1,p2]); + */ + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo4->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, array.GetTaggedValue()); + + [[maybe_unused]] auto prev4 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue result4 = BuiltinsPromise::All(ecmaRuntimeCallInfo4.get()); + JSHandle allPromise(thread, result4); + EXPECT_EQ(JSTaggedValue::SameValue(allPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(allPromise->GetPromiseResult().IsUndefined(), true); + + /** + * @tc.steps: step5. p3.then((resolve)=>{print(resolve)}, (reject)=>{print(reject)}); + */ + JSHandle nativeFuncRaceThenOnResolved = + factory->NewJSFunction(env, reinterpret_cast(TestPromiseAllThenOnResolved)); + auto ecmaRuntimeCallInfo5 = TestHelper::CreateEcmaRuntimeCallInfo(thread, allPromise.GetTaggedValue(), 8); + ecmaRuntimeCallInfo5->SetFunction(allPromise.GetTaggedValue()); + ecmaRuntimeCallInfo5->SetThis(allPromise.GetTaggedValue()); + ecmaRuntimeCallInfo5->SetCallArg(0, nativeFuncRaceThenOnResolved.GetTaggedValue()); + ecmaRuntimeCallInfo5->SetCallArg(1, nativeFuncRaceThenOnResolved.GetTaggedValue()); + + [[maybe_unused]] auto prev5 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo5.get()); + JSTaggedValue thenResult = BuiltinsPromise::Then(ecmaRuntimeCallInfo5.get()); + JSHandle thenPromise(thread, thenResult); + + EXPECT_TRUE(JSTaggedValue::SameValue(thenPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING)))); + EXPECT_TRUE(thenPromise->GetPromiseResult().IsUndefined()); + + /** + * @tc.steps: step6. execute promise queue + */ + auto microJobQueue = EcmaVM::Cast(instance)->GetMicroJobQueue(); + if (!thread->HasPendingException()) { + microJobQueue->ExecutePendingJob(thread); + } +} + +/* + * @tc.name: Catch + * @tc.desc: test Catch() method + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsPromiseTest, Catch) +{ + auto env = EcmaVM::Cast(instance)->GetGlobalEnv(); + auto factory = EcmaVM::Cast(instance)->GetFactory(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg1(thread, JSTaggedValue(3)); + + /** + * @tc.steps: step1. var p1 = Promise.reject(3) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg1.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = BuiltinsPromise::Reject(ecmaRuntimeCallInfo1.get()); + JSHandle rejectPromise(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), JSTaggedValue(3)), true); + + /** + * @tc.steps: step2. p1 invokes catch() + */ + JSHandle testPromiseCatch = factory->NewJSFunction(env, reinterpret_cast(TestPromiseCatch)); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, rejectPromise.GetTaggedValue(), 6); + ecmaRuntimeCallInfo2->SetFunction(rejectPromise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(rejectPromise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, testPromiseCatch.GetTaggedValue()); + + [[maybe_unused]] auto prevCatch = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue catchResult = BuiltinsPromise::Catch(ecmaRuntimeCallInfo2.get()); + JSHandle catchPromise(thread, catchResult); + + EXPECT_EQ(JSTaggedValue::SameValue(catchPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(catchPromise->GetPromiseResult().IsUndefined(), true); + + /** + * @tc.steps: step3. execute promise queue + */ + auto microJobQueue = EcmaVM::Cast(instance)->GetMicroJobQueue(); + if (!thread->HasPendingException()) { + microJobQueue->ExecutePendingJob(thread); + } +} + +/* + * @tc.name: ThenResolve + * @tc.desc: Testing the Then() function with the Resolve() function + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsPromiseTest, ThenResolve) +{ + auto env = EcmaVM::Cast(instance)->GetGlobalEnv(); + auto factory = EcmaVM::Cast(instance)->GetFactory(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg = JSHandle::Cast(factory->NewFromString("resolve")); + + /** + * @tc.steps: step1. var p1 = Promise.resolve("resolve") + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo1.get()); + JSHandle resolvePromise(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseResult(), paramMsg.GetTaggedValue()), true); + + /** + * @tc.steps: step2. p1 invokes then() + */ + JSHandle testPromiseThenOnResolved = + factory->NewJSFunction(env, reinterpret_cast(TestPromiseThenOnResolved)); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, resolvePromise.GetTaggedValue(), 8); + ecmaRuntimeCallInfo2->SetFunction(resolvePromise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(resolvePromise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, testPromiseThenOnResolved.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prevThen = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue thenResult = BuiltinsPromise::Then(ecmaRuntimeCallInfo2.get()); + JSHandle thenPromise(thread, thenResult); + + EXPECT_EQ(JSTaggedValue::SameValue(thenPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(thenPromise->GetPromiseResult().IsUndefined(), true); + + /** + * @tc.steps: step3. execute promise queue + */ + auto microJobQueue = EcmaVM::Cast(instance)->GetMicroJobQueue(); + if (!thread->HasPendingException()) { + microJobQueue->ExecutePendingJob(thread); + } +} + +/* + * @tc.name: ThenReject + * @tc.desc: Testing the Then() function with the Reject() function + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsPromiseTest, ThenReject) +{ + auto env = EcmaVM::Cast(instance)->GetGlobalEnv(); + auto factory = EcmaVM::Cast(instance)->GetFactory(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg = JSHandle::Cast(factory->NewFromString("reject")); + + /** + * @tc.steps: step1. var p1 = Promise.Reject(5) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = BuiltinsPromise::Reject(ecmaRuntimeCallInfo1.get()); + JSHandle rejectPromise(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), paramMsg.GetTaggedValue()), true); + + /** + * @tc.steps: step1. p1 invokes then() + */ + JSHandle testPromiseThenOnRejected = + factory->NewJSFunction(env, reinterpret_cast(TestPromiseThenOnRejected)); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, rejectPromise.GetTaggedValue(), 8); + ecmaRuntimeCallInfo2->SetFunction(rejectPromise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(rejectPromise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, testPromiseThenOnRejected.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(1, testPromiseThenOnRejected.GetTaggedValue()); + + [[maybe_unused]] auto prevThen = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue thenResult = BuiltinsPromise::Then(ecmaRuntimeCallInfo2.get()); + JSHandle thenPromise(thread, thenResult); + + EXPECT_EQ(JSTaggedValue::SameValue(thenPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(thenPromise->GetPromiseResult().IsUndefined(), true); + /** + * @tc.steps: step3. execute promise queue + */ + auto microJobQueue = EcmaVM::Cast(instance)->GetMicroJobQueue(); + if (!thread->HasPendingException()) { + microJobQueue->ExecutePendingJob(thread); + } +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_proxy_test.cpp b/ecmascript/builtins/tests/builtins_proxy_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2ae6072e5ae181b8e2238f153a084b59626ee9bd --- /dev/null +++ b/ecmascript/builtins/tests/builtins_proxy_test.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_proxy.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "file_items.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsProxyTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSHandle BuiltinsTestProxyCreate(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle objFun(globalEnv->GetObjectFunction()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); + return obj; +} + +// 26.2.1.1 Proxy( [ value ] ) +HWTEST_F_L0(BuiltinsProxyTest, ProxyConstructor) +{ + JSHandle target = BuiltinsTestProxyCreate(thread); + JSHandle handler = BuiltinsTestProxyCreate(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Null(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handler.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsProxy::ProxyConstructor(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); + JSHandle resultHandle(thread, result); + EXPECT_TRUE(resultHandle->IsJSProxy()); +} + +// 26.2.2.1 Proxy.revocable ( target, handler ) +HWTEST_F_L0(BuiltinsProxyTest, Revocable) +{ + JSHandle target = BuiltinsTestProxyCreate(thread); + JSHandle handler = BuiltinsTestProxyCreate(thread); + + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle proxyFun(globalEnv->GetProxyFunction()); + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("prop")); + PropertyDescriptor desc(thread); + desc.SetWritable(false); + JSObject::DefineOwnProperty(thread, handler, key, desc); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handler.GetTaggedValue()); + ecmaRuntimeCallInfo->SetNewTarget(JSTaggedValue(*proxyFun)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsProxy::Revocable(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); + JSHandle resultHandle(thread, result); + + auto globalConst = thread->GlobalConstants(); + JSHandle proxyKey = globalConst->GetHandledProxyString(); + JSHandle revokeKey = globalConst->GetHandledRevokeString(); + + JSHandle keys = JSObject::GetOwnPropertyKeys(thread, resultHandle); + bool pflag = false; + bool rflag = false; + for (array_size_t i = 0; i < keys->GetLength(); i++) { + if (JSTaggedValue::SameValue(keys->Get(i), proxyKey.GetTaggedValue())) { + pflag = true; + } + if (JSTaggedValue::SameValue(keys->Get(i), revokeKey.GetTaggedValue())) { + rflag = true; + } + } + EXPECT_TRUE(pflag); + EXPECT_TRUE(rflag); + + PropertyDescriptor descRes(thread); + JSObject::GetOwnProperty(thread, resultHandle, revokeKey, descRes); + EXPECT_TRUE(descRes.GetValue()->IsProxyRevocFunction()); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_reflect_test.cpp b/ecmascript/builtins/tests/builtins_reflect_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d8466f677ecc0807fad158193ffde93258e7f1c2 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_reflect_test.cpp @@ -0,0 +1,534 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_reflect.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/tagged_array-inl.h" + +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +using BuiltinsBase = panda::ecmascript::base::BuiltinsBase; +using JSArray = panda::ecmascript::JSArray; + +namespace panda::test { +class BuiltinsReflectTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +// use for create a JSObject of test +static JSHandle TestObjectCreate(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + return JSHandle::Cast(env->GetObjectFunction()); +} + +// native function for test Reflect.apply +JSTaggedValue TestReflectApply(EcmaRuntimeCallInfo *argv) +{ + auto thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + int result = 0; + for (array_size_t index = 0; index < argv->GetArgsNumber(); ++index) { + result += BuiltinsBase::GetCallArg(argv, index).GetTaggedValue().GetInt(); + } + JSHandle thisValue = BuiltinsBase::GetThis(argv); + + JSTaggedValue testA = + JSObject::GetProperty(thread, thisValue, + JSHandle(factory->NewFromString("test_reflect_apply_a"))) + .GetValue() + .GetTaggedValue(); + JSTaggedValue testB = + JSObject::GetProperty(thread, thisValue, + JSHandle(factory->NewFromString("test_reflect_apply_b"))) + .GetValue() + .GetTaggedValue(); + + result = result + testA.GetInt() + testB.GetInt(); + return BuiltinsBase::GetTaggedInt(result); +} + +// Reflect.apply (target, thisArgument, argumentsList) +HWTEST_F_L0(BuiltinsReflectTest, ReflectApply) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = factory->NewJSFunction(env, reinterpret_cast(TestReflectApply)); + // thisArgument + JSHandle thisArgument = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + JSObject::SetProperty(thread, JSHandle(thisArgument), + JSHandle(factory->NewFromString("test_reflect_apply_a")), + JSHandle(thread, JSTaggedValue(11))); + JSObject::SetProperty(thread, JSHandle(thisArgument), + JSHandle(factory->NewFromString("test_reflect_apply_b")), + JSHandle(thread, JSTaggedValue(22))); + // argumentsList + JSHandle argumentsList(JSArray::ArrayCreate(thread, JSTaggedNumber(2))); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(33))); + JSArray::DefineOwnProperty(thread, argumentsList, JSHandle(thread, JSTaggedValue(0)), desc); + + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(44))); + JSArray::DefineOwnProperty(thread, argumentsList, JSHandle(thread, JSTaggedValue(1)), desc1); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(*thisArgument)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(*argumentsList)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectApply(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(110).GetRawData()); + + JSObject::DeleteProperty(thread, (thisArgument), + JSHandle(factory->NewFromString("test_reflect_apply_a"))); + JSObject::DeleteProperty(thread, (thisArgument), + JSHandle(factory->NewFromString("test_reflect_apply_b"))); +} + +// Reflect.construct (target, argumentsList [ , newTarget]) +HWTEST_F_L0(BuiltinsReflectTest, ReflectConstruct) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = JSHandle::Cast(env->GetStringFunction()); + // argumentsList + JSHandle argumentsList(JSArray::ArrayCreate(thread, JSTaggedNumber(1))); + PropertyDescriptor desc(thread, JSHandle::Cast(factory->NewFromString("ReflectConstruct"))); + JSArray::DefineOwnProperty(thread, argumentsList, JSHandle(thread, JSTaggedValue(0)), desc); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(*argumentsList)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectConstruct(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle taggedResult(thread, result); + JSHandle refResult = JSHandle::Cast(taggedResult); + JSHandle ruler = factory->NewFromString("ReflectConstruct"); + ASSERT_EQ(EcmaString::Cast(refResult->GetValue().GetTaggedObject())->Compare(*ruler), 0); +} + +// Reflect.defineProperty (target, propertyKey, attributes) +HWTEST_F_L0(BuiltinsReflectTest, ReflectDefineProperty) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // propertyKey + JSHandle key(factory->NewFromString("test_reflect_define_property")); + // attributes + JSHandle attributes = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // attributes value + auto globalConst = thread->GlobalConstants(); + JSHandle valueKey = globalConst->GetHandledValueString(); + JSHandle value(thread, JSTaggedValue(100)); + JSObject::SetProperty(thread, JSHandle(attributes), valueKey, value); + // attributes writable + JSHandle writableKey = globalConst->GetHandledWritableString(); + JSHandle writable(thread, JSTaggedValue::True()); + JSObject::SetProperty(thread, JSHandle(attributes), writableKey, writable); + // attributes enumerable + JSHandle enumerableKey = globalConst->GetHandledEnumerableString(); + JSHandle enumerable(thread, JSTaggedValue::False()); + JSObject::SetProperty(thread, JSHandle(attributes), enumerableKey, enumerable); + // attributes configurable + JSHandle configurableKey = globalConst->GetHandledConfigurableString(); + JSHandle configurable(thread, JSTaggedValue::True()); + JSObject::SetProperty(thread, JSHandle(attributes), configurableKey, configurable); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(*attributes)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectDefineProperty(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + + PropertyDescriptor descRuler(thread); + JSObject::GetOwnProperty(thread, target, key, descRuler); + ASSERT_EQ(descRuler.GetValue()->GetInt(), 100); + ASSERT_EQ(descRuler.IsWritable(), true); + ASSERT_EQ(descRuler.IsEnumerable(), false); + ASSERT_EQ(descRuler.IsConfigurable(), true); +} + +// Reflect.deleteProperty (target, propertyKey) +HWTEST_F_L0(BuiltinsReflectTest, ReflectDeleteProperty) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // propertyKey + JSHandle key(factory->NewFromString("test_reflect_delete_property")); + JSHandle value(thread, JSTaggedValue(101)); + JSObject::SetProperty(thread, JSHandle(target), key, value); + + PropertyDescriptor desc(thread); + ASSERT_EQ(JSObject::GetOwnProperty(thread, target, key, desc), true); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectDeleteProperty(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + ASSERT_EQ(JSObject::GetOwnProperty(thread, target, key, desc), false); +} + +// Reflect.get (target, propertyKey [ , receiver]) +HWTEST_F_L0(BuiltinsReflectTest, ReflectGet) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // propertyKey + JSHandle key(factory->NewFromString("test_reflect_get")); + // set property + JSHandle value(thread, JSTaggedValue(101.5)); + JSObject::SetProperty(thread, JSHandle(target), key, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectGet(ecmaRuntimeCallInfo.get()); + + JSHandle resultValue(thread, result); + ASSERT_EQ(resultValue->GetDouble(), 101.5); +} + +// Reflect.getOwnPropertyDescriptor ( target, propertyKey ) +HWTEST_F_L0(BuiltinsReflectTest, ReflectGetOwnPropertyDescriptor) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // propertyKey + JSHandle key(factory->NewFromString("test_reflect_get_property_descriptor")); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(102)), true, false, true); + ASSERT_EQ(JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(target), key, desc), true); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectGetOwnPropertyDescriptor(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + + JSHandle resultObj(thread, result); + // test value + auto globalConst = thread->GlobalConstants(); + JSHandle valueKey = globalConst->GetHandledValueString(); + JSHandle resultValue = JSObject::GetProperty(thread, resultObj, valueKey).GetValue(); + ASSERT_EQ(resultValue->GetInt(), 102); + // test writable + JSHandle writableKey = globalConst->GetHandledWritableString(); + JSHandle resultWritable = JSObject::GetProperty(thread, resultObj, writableKey).GetValue(); + ASSERT_EQ(resultWritable->ToBoolean(), true); + // test enumerable + JSHandle enumerableKey = globalConst->GetHandledEnumerableString(); + JSHandle resultEnumerable = JSObject::GetProperty(thread, resultObj, enumerableKey).GetValue(); + ASSERT_EQ(resultEnumerable->ToBoolean(), false); + // test configurable + JSHandle configurableKey = globalConst->GetHandledConfigurableString(); + JSHandle resultConfigurable = JSObject::GetProperty(thread, resultObj, configurableKey).GetValue(); + ASSERT_EQ(resultConfigurable->ToBoolean(), true); +} + +// Reflect.getPrototypeOf (target) +HWTEST_F_L0(BuiltinsReflectTest, ReflectGetPrototypeOf) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + JSHandle proto = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + + ASSERT_EQ(JSObject::SetPrototype(thread, target, JSHandle(proto)), true); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectGetPrototypeOf(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); + JSHandle resultObj(thread, JSTaggedValue(reinterpret_cast(result.GetRawData()))); + ASSERT_EQ(JSTaggedValue::SameValue(resultObj.GetTaggedValue(), proto.GetTaggedValue()), true); +} + +// Reflect.has (target, propertyKey) +HWTEST_F_L0(BuiltinsReflectTest, ReflectHas) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // propertyKey + JSHandle key(factory->NewFromString("test_reflect_has")); + JSHandle value(thread, JSTaggedValue(103)); + ASSERT_EQ(JSObject::SetProperty(thread, JSHandle(target), key, value), true); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectHas(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// Reflect.isExtensible (target) +HWTEST_F_L0(BuiltinsReflectTest, ReflectIsExtensible) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + target->GetJSHClass()->SetExtensible(false); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectIsExtensible(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Reflect.ownKeys (target) +HWTEST_F_L0(BuiltinsReflectTest, ReflectOwnKeys) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + JSHandle key0(factory->NewFromString("test_reflect_own_keys1")); + JSHandle value0(thread, JSTaggedValue(104)); + ASSERT_EQ(JSObject::SetProperty(thread, JSHandle(target), key0, value0), true); + JSHandle key1(factory->NewFromString("test_reflect_own_keys2")); + JSHandle value1(thread, JSTaggedValue(105)); + ASSERT_EQ(JSObject::SetProperty(thread, JSHandle(target), key1, value1), true); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectOwnKeys(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle resultTaggedValue(thread, reinterpret_cast(result.GetRawData())); + JSHandle resultArray = JSHandle::Cast(resultTaggedValue); + // test length + JSHandle resultLengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle resultLength = + JSObject::GetProperty(thread, JSHandle(resultArray), resultLengthKey).GetValue(); + ASSERT_EQ(resultLength->GetInt(), 2); + // test array[0] + JSHandle resultKey0(thread, JSTaggedValue(0)); + JSHandle resultValue0 = + JSObject::GetProperty(thread, JSHandle(resultArray), resultKey0).GetValue(); + ASSERT_EQ(reinterpret_cast(resultValue0.GetTaggedValue().GetTaggedObject()) + ->Compare(reinterpret_cast(key0.GetTaggedValue().GetTaggedObject())), + 0); + // test array[1] + JSHandle resultKey1(thread, JSTaggedValue(1)); + JSHandle resultValue1 = + JSObject::GetProperty(thread, JSHandle(resultArray), resultKey1).GetValue(); + ASSERT_EQ(reinterpret_cast(resultValue1.GetTaggedValue().GetTaggedObject()) + ->Compare(reinterpret_cast(key1.GetTaggedValue().GetTaggedObject())), + 0); +} + +// Reflect.preventExtensions (target) +HWTEST_F_L0(BuiltinsReflectTest, ReflectPreventExtensions) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + target->GetJSHClass()->SetExtensible(true); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectPreventExtensions(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + ASSERT_EQ(target->IsExtensible(), false); +} + +// Reflect.set (target, propertyKey, V [ , receiver]) +HWTEST_F_L0(BuiltinsReflectTest, ReflectSet) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // propertyKey + JSHandle key(factory->NewFromString("test_reflect_set")); + // value + JSHandle value(thread, JSTaggedValue(106)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, value.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectSet(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + + JSHandle ruler = JSObject::GetProperty(thread, JSHandle(target), key).GetValue(); + ASSERT_EQ(JSTaggedValue::ToInt32(thread, ruler), 106); +} + +// Reflect.setPrototypeOf (target, proto) +HWTEST_F_L0(BuiltinsReflectTest, ReflectSetPrototypeOf) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + JSHandle proto = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, proto.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectSetPrototypeOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + JSHandle resultObj(thread, target->GetJSHClass()->GetPrototype()); + ASSERT_EQ(JSTaggedValue::SameValue(resultObj.GetTaggedValue(), proto.GetTaggedValue()), true); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_regexp_test.cpp b/ecmascript/builtins/tests/builtins_regexp_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ea39a66fa11ccbef6715dcf58a4140eccf40225b --- /dev/null +++ b/ecmascript/builtins/tests/builtins_regexp_test.cpp @@ -0,0 +1,644 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_regexp.h" + +#include "ecmascript/builtins/builtins_regexp.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/regexp/regexp_parser_cache.h" + +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsRegExpTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + JSTaggedValue CreateRegExpObjByPatternAndFlags(JSThread *thread, const JSHandle &pattern, + const JSHandle &flags) + { + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle regexp(env->GetRegExpFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + // make dyn_runtime_call_info + // 8 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*regexp), 8); + ecmaRuntimeCallInfo->SetFunction(regexp.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, pattern.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, flags.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke RegExpConstructor method + JSTaggedValue result = BuiltinsRegExp::RegExpConstructor(ecmaRuntimeCallInfo.get()); + return result; + } +}; + +HWTEST_F_L0(BuiltinsRegExpTest, RegExpConstructor1) +{ + // invoke RegExpConstructor method + JSHandle pattern = thread->GetEcmaVM()->GetFactory()->NewFromString("\\w+"); + JSHandle flags = thread->GetEcmaVM()->GetFactory()->NewFromString("i"); + JSTaggedValue result = CreateRegExpObjByPatternAndFlags(thread, pattern, flags); + + // ASSERT IsRegExp() + JSHandle regexpObject(thread, result); + ASSERT_TRUE(JSObject::IsRegExp(thread, regexpObject)); + + JSHandle jsRegexp(thread, JSRegExp::Cast(regexpObject->GetTaggedObject())); + JSHandle originalSource(thread, jsRegexp->GetOriginalSource()); + JSHandle originalFlags(thread, jsRegexp->GetOriginalFlags()); + ASSERT_EQ(static_cast(originalSource->GetTaggedObject())->Compare(*pattern), 0); + ASSERT_EQ(static_cast(originalFlags->GetTaggedObject())->Compare(*flags), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, RegExpConstructor2) +{ + // invoke RegExpConstructor method + JSHandle pattern = thread->GetEcmaVM()->GetFactory()->NewFromString("\\w+"); + JSHandle flags = thread->GetEcmaVM()->GetFactory()->NewFromString("i"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern, flags); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle regexp(env->GetRegExpFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*regexp), 8); + ecmaRuntimeCallInfo->SetFunction(regexp.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke RegExpConstructor method + JSTaggedValue result2 = BuiltinsRegExp::RegExpConstructor(ecmaRuntimeCallInfo.get()); + + // ASSERT IsRegExp() + JSHandle regexpObject(thread, result2); + ASSERT_TRUE(JSObject::IsRegExp(thread, regexpObject)); + + JSHandle jsRegexp(thread, JSRegExp::Cast(regexpObject->GetTaggedObject())); + JSHandle originalSource(thread, jsRegexp->GetOriginalSource()); + JSHandle originalFlags(thread, jsRegexp->GetOriginalFlags()); + ASSERT_EQ(static_cast(originalSource->GetTaggedObject())->Compare(*pattern), 0); + ASSERT_EQ(static_cast(originalFlags->GetTaggedObject())->Compare(*flags), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, RegExpConstructor3) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("\\w+"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("i"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle regexp(env->GetRegExpFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + JSHandle flags2 = thread->GetEcmaVM()->GetFactory()->NewFromString("gi"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*regexp), 8); + ecmaRuntimeCallInfo->SetFunction(regexp.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, flags2.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke RegExpConstructor method + JSTaggedValue result2 = BuiltinsRegExp::RegExpConstructor(ecmaRuntimeCallInfo.get()); + + // ASSERT IsRegExp() + JSHandle regexpObject(thread, result2); + ASSERT_TRUE(JSObject::IsRegExp(thread, regexpObject)); + + JSHandle jsRegexp(thread, JSRegExp::Cast(regexpObject->GetTaggedObject())); + JSHandle originalSource(thread, jsRegexp->GetOriginalSource()); + JSHandle originalFlags(thread, jsRegexp->GetOriginalFlags()); + ASSERT_EQ(static_cast(originalSource->GetTaggedObject())->Compare(*pattern1), 0); + ASSERT_EQ(static_cast(originalFlags->GetTaggedObject())->Compare(*flags2), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, GetSource1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString(""); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("i"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle result1Handle(thread, result1); + + // invoke GetSource method + JSHandle source(thread, thread->GetEcmaVM()->GetFactory()->NewFromString("source").GetTaggedValue()); + JSHandle sourceResult(JSObject::GetProperty(thread, result1Handle, source).GetValue()); + + JSHandle expect = thread->GetEcmaVM()->GetFactory()->NewFromString("(?:)"); + ASSERT_EQ(static_cast(sourceResult->GetTaggedObject())->Compare(*expect), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, GetSource2) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("/w+"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("i"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle result1Handle(thread, result1); + + // invoke GetSource method + JSHandle source(thread->GetEcmaVM()->GetFactory()->NewFromString("source")); + JSHandle sourceResult(JSObject::GetProperty(thread, result1Handle, source).GetValue()); + + JSHandle expect = thread->GetEcmaVM()->GetFactory()->NewFromString("\\/w+"); + ASSERT_EQ(static_cast(sourceResult->GetTaggedObject())->Compare(*expect), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, Get) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("\\w+"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("gimuy"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle result1Handle(thread, result1); + + JSHandle global(thread->GetEcmaVM()->GetFactory()->NewFromString("global")); + JSTaggedValue taggedGlobalResult = + JSTaggedValue(JSObject::GetProperty(thread, result1Handle, global).GetValue().GetTaggedValue()); + ASSERT_EQ(taggedGlobalResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + JSHandle ignoreCase(thread->GetEcmaVM()->GetFactory()->NewFromString("ignoreCase")); + JSTaggedValue taggedIgnoreCaseResult = + JSTaggedValue(JSObject::GetProperty(thread, result1Handle, ignoreCase).GetValue().GetTaggedValue()); + ASSERT_EQ(taggedIgnoreCaseResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + JSHandle multiline(thread->GetEcmaVM()->GetFactory()->NewFromString("multiline")); + JSTaggedValue taggedMultilineResult = + JSTaggedValue(JSObject::GetProperty(thread, result1Handle, multiline).GetValue().GetTaggedValue()); + ASSERT_EQ(taggedMultilineResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + JSHandle sticky(thread->GetEcmaVM()->GetFactory()->NewFromString("sticky")); + JSTaggedValue taggedStickyResult = + JSTaggedValue(JSObject::GetProperty(thread, result1Handle, sticky).GetValue().GetTaggedValue()); + ASSERT_EQ(taggedStickyResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + JSHandle unicode(thread->GetEcmaVM()->GetFactory()->NewFromString("unicode")); + JSTaggedValue taggedUnicodeResult = + JSTaggedValue(JSObject::GetProperty(thread, result1Handle, unicode).GetValue().GetTaggedValue()); + ASSERT_EQ(taggedUnicodeResult.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +HWTEST_F_L0(BuiltinsRegExpTest, GetFlags) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("\\w+"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("imuyg"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle result1Handle(thread, result1); + + // invoke GetFlags method + JSHandle flags(thread->GetEcmaVM()->GetFactory()->NewFromString("flags")); + JSHandle flagsResult(JSObject::GetProperty(thread, result1Handle, flags).GetValue()); + + JSHandle expectResult = thread->GetEcmaVM()->GetFactory()->NewFromString("gimuy"); + ASSERT_EQ(static_cast(flagsResult->GetTaggedObject())->Compare(*expectResult), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, toString) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("\\w+"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("imuyg"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke ToString method + JSTaggedValue toStringResult = BuiltinsRegExp::ToString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(toStringResult.IsString()); + JSHandle toStringResultHandle(thread, toStringResult); + JSHandle expectResult = thread->GetEcmaVM()->GetFactory()->NewFromString("/\\w+/gimuy"); + ASSERT_EQ(static_cast(toStringResultHandle->GetTaggedObject())->Compare(*expectResult), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, Exec1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("quick\\s(brown).+?(jumps)"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("ig"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = + thread->GetEcmaVM()->GetFactory()->NewFromString("The Quick Brown Fox Jumps Over The Lazy Dog"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Exec method + JSTaggedValue results = BuiltinsRegExp::Exec(ecmaRuntimeCallInfo.get()); + + JSHandle execResult(thread, results); + JSHandle resultZero = thread->GetEcmaVM()->GetFactory()->NewFromString("Quick Brown Fox Jumps"); + JSHandle resultOne = thread->GetEcmaVM()->GetFactory()->NewFromString("Brown"); + JSHandle resultTwo = thread->GetEcmaVM()->GetFactory()->NewFromString("Jumps"); + + JSHandle index(thread->GetEcmaVM()->GetFactory()->NewFromString("index")); + JSHandle indexHandle(JSObject::GetProperty(thread, execResult, index).GetValue()); + uint32_t resultIndex = JSTaggedValue::ToUint32(thread, indexHandle); + ASSERT_TRUE(resultIndex == 4); + + JSHandle input(thread->GetEcmaVM()->GetFactory()->NewFromString("input")); + + JSHandle inputHandle(JSObject::GetProperty(thread, execResult, input).GetValue()); + JSHandle outputInput = JSTaggedValue::ToString(thread, inputHandle); + ASSERT_EQ(outputInput->Compare(*inputString), 0); + + JSHandle zero(thread->GetEcmaVM()->GetFactory()->NewFromString("0")); + JSHandle zeroHandle(JSObject::GetProperty(thread, execResult, zero).GetValue()); + JSHandle outputZero = JSTaggedValue::ToString(thread, zeroHandle); + ASSERT_EQ(outputZero->Compare(*resultZero), 0); + + JSHandle first(thread->GetEcmaVM()->GetFactory()->NewFromString("1")); + JSHandle oneHandle(JSObject::GetProperty(thread, execResult, first).GetValue()); + JSHandle outputOne = JSTaggedValue::ToString(thread, oneHandle); + ASSERT_EQ(outputOne->Compare(*resultOne), 0); + + JSHandle second(thread->GetEcmaVM()->GetFactory()->NewFromString("2")); + JSHandle twoHandle(JSObject::GetProperty(thread, execResult, second).GetValue()); + JSHandle outputTwo = JSTaggedValue::ToString(thread, twoHandle); + ASSERT_EQ(outputTwo->Compare(*resultTwo), 0); + + JSHandle regexp = JSHandle::Cast(value); + JSHandle lastIndexHandle(thread->GetEcmaVM()->GetFactory()->NewFromString("lastIndex")); + JSHandle lastIndexObj(JSObject::GetProperty(thread, regexp, lastIndexHandle).GetValue()); + int lastIndex = lastIndexObj->GetInt(); + ASSERT_TRUE(lastIndex == 25); +} + +HWTEST_F_L0(BuiltinsRegExpTest, Exec2) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("((1)|(12))((3)|(23))"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("ig"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = thread->GetEcmaVM()->GetFactory()->NewFromString("123"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Exec method + JSTaggedValue results = BuiltinsRegExp::Exec(ecmaRuntimeCallInfo.get()); + + JSHandle execResult(thread, results); + JSHandle resultZero = thread->GetEcmaVM()->GetFactory()->NewFromString("123"); + JSHandle resultOne = thread->GetEcmaVM()->GetFactory()->NewFromString("1"); + JSHandle resultTwo = thread->GetEcmaVM()->GetFactory()->NewFromString("1"); + JSHandle resultFour = thread->GetEcmaVM()->GetFactory()->NewFromString("23"); + JSHandle resultSix = thread->GetEcmaVM()->GetFactory()->NewFromString("23"); + + JSHandle index(thread->GetEcmaVM()->GetFactory()->NewFromString("index")); + JSHandle indexHandle(JSObject::GetProperty(thread, execResult, index).GetValue()); + uint32_t resultIndex = JSTaggedValue::ToUint32(thread, indexHandle); + ASSERT_TRUE(resultIndex == 0); + + JSHandle input(thread->GetEcmaVM()->GetFactory()->NewFromString("input")); + JSHandle inputHandle(JSObject::GetProperty(thread, execResult, input).GetValue()); + JSHandle outputInput = JSTaggedValue::ToString(thread, inputHandle); + ASSERT_EQ(outputInput->Compare(*inputString), 0); + + JSHandle zero(thread->GetEcmaVM()->GetFactory()->NewFromString("0")); + JSHandle zeroHandle(JSObject::GetProperty(thread, execResult, zero).GetValue()); + JSHandle outputZero = JSTaggedValue::ToString(thread, zeroHandle); + ASSERT_EQ(outputZero->Compare(*resultZero), 0); + + JSHandle first(thread->GetEcmaVM()->GetFactory()->NewFromString("1")); + JSHandle oneHandle(JSObject::GetProperty(thread, execResult, first).GetValue()); + JSHandle outputOne = JSTaggedValue::ToString(thread, oneHandle); + ASSERT_EQ(outputOne->Compare(*resultOne), 0); + + JSHandle second(thread->GetEcmaVM()->GetFactory()->NewFromString("2")); + JSHandle twoHandle(JSObject::GetProperty(thread, execResult, second).GetValue()); + JSHandle outputTwo = JSTaggedValue::ToString(thread, twoHandle); + ASSERT_EQ(outputTwo->Compare(*resultTwo), 0); + + JSHandle regexp = JSHandle::Cast(value); + JSHandle lastIndexHandle(thread->GetEcmaVM()->GetFactory()->NewFromString("lastIndex")); + JSHandle lastIndexObj(JSObject::GetProperty(thread, regexp, lastIndexHandle).GetValue()); + int lastIndex = lastIndexObj->GetInt(); + ASSERT_TRUE(lastIndex == 3); + + JSHandle third(thread->GetEcmaVM()->GetFactory()->NewFromString("3")); + JSHandle thirdHandle(JSObject::GetProperty(thread, execResult, third).GetValue()); + ASSERT_TRUE(thirdHandle->IsUndefined()); + + JSHandle four(thread->GetEcmaVM()->GetFactory()->NewFromString("4")); + JSHandle fourHandle(JSObject::GetProperty(thread, execResult, four).GetValue()); + JSHandle outputFour = JSTaggedValue::ToString(thread, fourHandle); + ASSERT_EQ(outputFour->Compare(*resultFour), 0); + + JSHandle five(thread->GetEcmaVM()->GetFactory()->NewFromString("5")); + JSHandle fiveHandle(JSObject::GetProperty(thread, execResult, five).GetValue()); + ASSERT_TRUE(fiveHandle->IsUndefined()); + + JSHandle six(thread->GetEcmaVM()->GetFactory()->NewFromString("6")); + JSHandle sixHandle(JSObject::GetProperty(thread, execResult, six).GetValue()); + JSHandle outputSix = JSTaggedValue::ToString(thread, sixHandle); + ASSERT_EQ(outputSix->Compare(*resultSix), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, Match1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("quick\\s(brown).+?(jumps)"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = + thread->GetEcmaVM()->GetFactory()->NewFromString("The Quick Brown Fox Jumps Over The Lazy Dog"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Match method + JSTaggedValue matchResults = BuiltinsRegExp::Match(ecmaRuntimeCallInfo.get()); + + JSHandle matchResult(thread, matchResults); + JSHandle zero(thread->GetEcmaVM()->GetFactory()->NewFromString("0")); + JSHandle resultZero = thread->GetEcmaVM()->GetFactory()->NewFromString("Quick Brown Fox Jumps"); + JSHandle zeroHandle(JSObject::GetProperty(thread, matchResult, zero).GetValue()); + JSHandle outputZero = JSTaggedValue::ToString(thread, zeroHandle); + ASSERT_EQ(outputZero->Compare(*resultZero), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, Test1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("quick\\s(brown).+?(jumps)"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = + thread->GetEcmaVM()->GetFactory()->NewFromString("The Quick Brown Fox Jumps Over The Lazy Dog"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Test method + JSTaggedValue testResult = BuiltinsRegExp::Test(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(testResult.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +HWTEST_F_L0(BuiltinsRegExpTest, Search1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("quick\\s(brown).+?(jumps)"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = + thread->GetEcmaVM()->GetFactory()->NewFromString("The Quick Brown Fox Jumps Over The Lazy Dog"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Search method + JSTaggedValue searchResult = BuiltinsRegExp::Search(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(searchResult.GetRawData(), JSTaggedValue(4).GetRawData()); +} + +HWTEST_F_L0(BuiltinsRegExpTest, Split1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("-"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = thread->GetEcmaVM()->GetFactory()->NewFromString(""); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Split method + JSTaggedValue splitResults = BuiltinsRegExp::Split(ecmaRuntimeCallInfo.get()); + JSHandle splitResult(thread, splitResults); + + JSHandle zero(thread->GetEcmaVM()->GetFactory()->NewFromString("0")); + JSHandle zeroHandle(JSObject::GetProperty(thread, splitResult, zero).GetValue()); + JSHandle outputZero = JSTaggedValue::ToString(thread, zeroHandle); + ASSERT_EQ(outputZero->Compare(*inputString), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, Split2) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("-"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = thread->GetEcmaVM()->GetFactory()->NewFromString("a-b-c"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Split method + JSTaggedValue splitResults = BuiltinsRegExp::Split(ecmaRuntimeCallInfo.get()); + JSHandle splitResult(thread, splitResults); + JSHandle resultZero = thread->GetEcmaVM()->GetFactory()->NewFromString("a"); + JSHandle resultOne = thread->GetEcmaVM()->GetFactory()->NewFromString("b"); + JSHandle resultTwo = thread->GetEcmaVM()->GetFactory()->NewFromString("c"); + + JSHandle zero(thread->GetEcmaVM()->GetFactory()->NewFromString("0")); + JSHandle zeroHandle(JSObject::GetProperty(thread, splitResult, zero).GetValue()); + JSHandle outputZero = JSTaggedValue::ToString(thread, zeroHandle); + ASSERT_EQ(outputZero->Compare(*resultZero), 0); + + JSHandle first(thread->GetEcmaVM()->GetFactory()->NewFromString("1")); + JSHandle oneHandle(JSObject::GetProperty(thread, splitResult, first).GetValue()); + JSHandle outputOne = JSTaggedValue::ToString(thread, oneHandle); + ASSERT_EQ(outputOne->Compare(*resultOne), 0); + + JSHandle second(thread->GetEcmaVM()->GetFactory()->NewFromString("2")); + JSHandle twoHandle(JSObject::GetProperty(thread, splitResult, second).GetValue()); + JSHandle outputTwo = JSTaggedValue::ToString(thread, twoHandle); + ASSERT_EQ(outputTwo->Compare(*resultTwo), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, GetSpecies) +{ + // invoke RegExpConstructor method + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + EXPECT_TRUE(!speciesSymbol.GetTaggedValue().IsUndefined()); + + JSHandle newTarget(env->GetRegExpFunction()); + + JSTaggedValue value = + JSObject::GetProperty(thread, JSHandle(newTarget), speciesSymbol).GetValue().GetTaggedValue(); + EXPECT_EQ(value, newTarget.GetTaggedValue()); +} + +HWTEST_F_L0(BuiltinsRegExpTest, Replace1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("quick\\s(brown).+?(jumps)"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = + thread->GetEcmaVM()->GetFactory()->NewFromString("The Quick Brown Fox Jumps Over The Lazy Dog"); + JSHandle replaceString = + thread->GetEcmaVM()->GetFactory()->NewFromString("$&a $` $\' $2 $01 $$1 $21 $32 a"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke replace method + JSTaggedValue results = BuiltinsRegExp::Replace(ecmaRuntimeCallInfo.get()); + JSHandle replaceResult(thread, results); + JSHandle resultZero = thread->GetEcmaVM()->GetFactory()->NewFromString( + "The Quick Brown Fox Jumpsa The Over The Lazy Dog Jumps Brown $1 Jumps1 $32 a Over The Lazy Dog"); + ASSERT_EQ(static_cast(replaceResult->GetTaggedObject())->Compare(*resultZero), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, Replace2) +{ + // invoke RegExpConstructor method + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle pattern1 = factory->NewFromString("b(c)(z)?(.)"); + JSHandle flags1 = factory->NewFromString(""); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = factory->NewFromString("abcde"); + JSHandle replaceString = factory->NewFromString("[$01$02$03$04$00]"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke replace method + JSTaggedValue results = BuiltinsRegExp::Replace(ecmaRuntimeCallInfo.get()); + JSHandle replaceResult(thread, results); + JSHandle resultZero = factory->NewFromString("a[cd$04$00]e"); + ASSERT_EQ(static_cast(replaceResult->GetTaggedObject())->Compare(*resultZero), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, Replace3) +{ + // invoke RegExpConstructor method + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle pattern1 = factory->NewFromString("abc"); + JSHandle flags1 = factory->NewFromString("g"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = factory->NewFromString("abcde"); + JSHandle replaceString = factory->NewFromString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke replace method + JSTaggedValue results = BuiltinsRegExp::Replace(ecmaRuntimeCallInfo.get()); + JSHandle replaceResult(thread, results); + JSHandle resultZero = factory->NewFromString("de"); + ASSERT_EQ(static_cast(replaceResult->GetTaggedObject())->Compare(*resultZero), 0); +} + +HWTEST_F_L0(BuiltinsRegExpTest, RegExpParseCache) +{ + RegExpParserCache *regExpParserCache = thread->GetEcmaVM()->GetRegExpParserCache(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle string1 = factory->NewFromString("abc"); + JSHandle string2 = factory->NewFromString("abcd"); + regExpParserCache->SetCache(*string1, 0, JSTaggedValue::True(), 2); + ASSERT_TRUE(regExpParserCache->GetCache(*string1, 0).first == JSTaggedValue::True()); + ASSERT_TRUE(regExpParserCache->GetCache(*string1, 0).second == 2); + ASSERT_TRUE(regExpParserCache->GetCache(*string1, RegExpParserCache::CACHE_SIZE).first == JSTaggedValue::Hole()); + ASSERT_TRUE(regExpParserCache->GetCache(*string2, 0).first == JSTaggedValue::Hole()); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_set_test.cpp b/ecmascript/builtins/tests/builtins_set_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f1adc2fec35d676b8a8edc4a13497e76d4034cff --- /dev/null +++ b/ecmascript/builtins/tests/builtins_set_test.cpp @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_set.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" + +#include "ecmascript/js_array.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using BuiltinsSet = ecmascript::builtins::BuiltinsSet; +using JSSet = ecmascript::JSSet; + +class BuiltinsSetTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestFunc(EcmaRuntimeCallInfo *argv) + { + JSTaggedValue key = GetCallArg(argv, 0).GetTaggedValue(); + if (key.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + JSArray *jsArray = JSArray::Cast(GetThis(argv)->GetTaggedObject()); + int length = jsArray->GetArrayLength() + 1; + jsArray->SetArrayLength(argv->GetThread(), length); + return JSTaggedValue::Undefined(); + } + }; +}; + +JSSet *CreateBuiltinsSet(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsSetFunction()); + // 4 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 4); + ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSet::SetConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + return JSSet::Cast(reinterpret_cast(result.GetRawData())); +} +// new Set("abrupt").toString() +HWTEST_F_L0(BuiltinsSetTest, CreateAndGetSize) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsSetFunction()); + JSHandle set(thread, CreateBuiltinsSet(thread)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSet::GetSize(ecmaRuntimeCallInfo.get()); + + EXPECT_EQ(result.GetRawData(), JSTaggedValue(0).GetRawData()); + } + + JSHandle array(factory->NewTaggedArray(5)); + for (int i = 0; i < 5; i++) { + array->Set(thread, i, JSTaggedValue(i)); + } + + JSHandle values = JSArray::CreateArrayFromList(thread, array); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, values.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetNewTarget(newTarget.GetTaggedValue()); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsSet::SetConstructor(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(JSSet::Cast(reinterpret_cast(result1.GetRawData()))->GetSize(), 5); + } +} + +HWTEST_F_L0(BuiltinsSetTest, AddAndHas) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // create jsSet + JSHandle set(thread, CreateBuiltinsSet(thread)); + JSHandle key(thread, factory->NewFromString("key").GetTaggedValue()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsSet::Has(ecmaRuntimeCallInfo.get()); + + EXPECT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData()); + + // test Add() + JSTaggedValue result2 = BuiltinsSet::Add(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result2.IsECMAObject()); + JSSet *jsSet = JSSet::Cast(reinterpret_cast(result2.GetRawData())); + EXPECT_EQ(jsSet->GetSize(), 1); + + // test Has() + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(jsSet)); + ecmaRuntimeCallInfo1->SetCallArg(0, key.GetTaggedValue()); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result3 = BuiltinsSet::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + } + + // test -0.0 + JSHandle negativeZero(thread, JSTaggedValue(-0.0)); + JSHandle positiveZero(thread, JSTaggedValue(+0.0)); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(JSTaggedValue(jsSet)); + ecmaRuntimeCallInfo2->SetCallArg(0, negativeZero.GetTaggedValue()); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + BuiltinsSet::Add(ecmaRuntimeCallInfo.get()); + } + + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(JSTaggedValue(jsSet)); + ecmaRuntimeCallInfo3->SetCallArg(0, positiveZero.GetTaggedValue()); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + JSTaggedValue result4 = BuiltinsSet::Has(ecmaRuntimeCallInfo3.get()); + + EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); + } +} + +HWTEST_F_L0(BuiltinsSetTest, ForEach) +{ + // generate a set has 5 entry{key1,key2,key3,key4,key5} + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle set(thread, CreateBuiltinsSet(thread)); + char keyArray[] = "key0"; + for (int i = 0; i < 5; i++) { + keyArray[3] = '1' + i; + JSHandle key(factory->NewFromString(keyArray)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsSet::Add(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result1.IsECMAObject()); + JSSet *jsSet = JSSet::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsSet->GetSize(), i + 1); + } + // test foreach; + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = + factory->NewJSFunction(thread->GetEcmaVM()->GetGlobalEnv(), reinterpret_cast(TestClass::TestFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsSet::ForEach(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + EXPECT_EQ(jsArray->GetArrayLength(), 5); +} + +HWTEST_F_L0(BuiltinsSetTest, DeleteAndRemove) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // create jsSet + JSHandle set(thread, CreateBuiltinsSet(thread)); + + // add 40 keys + char keyArray[] = "key0"; + for (int i = 0; i < 40; i++) { + keyArray[3] = '1' + i; + JSHandle key(factory->NewFromString(keyArray)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsSet::Add(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result1.IsECMAObject()); + JSSet *jsSet = JSSet::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsSet->GetSize(), i + 1); + } + // whether jsSet has delete key + keyArray[3] = '1' + 8; + JSHandle deleteKey(factory->NewFromString(keyArray)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, deleteKey.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsSet::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); + + // delete + JSTaggedValue result3 = BuiltinsSet::Delete(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + + // check deleteKey is deleted + JSTaggedValue result4 = BuiltinsSet::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); + + JSTaggedValue result5 = BuiltinsSet::GetSize(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result5.GetRawData(), JSTaggedValue(39).GetRawData()); + + // clear + JSTaggedValue result6 = BuiltinsSet::Clear(ecmaRuntimeCallInfo1.get()); + EXPECT_EQ(result6.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + EXPECT_EQ(set->GetSize(), 0); +} + +HWTEST_F_L0(BuiltinsSetTest, Species) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle set(thread, CreateBuiltinsSet(thread)); + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + EXPECT_TRUE(!speciesSymbol->IsUndefined()); + + JSHandle newTarget(env->GetBuiltinsSetFunction()); + + JSTaggedValue value = + JSObject::GetProperty(thread, JSHandle(newTarget), speciesSymbol).GetValue().GetTaggedValue(); + JSHandle valueHandle(thread, value); + EXPECT_EQ(value, newTarget.GetTaggedValue()); + + // to string tag + JSHandle toStringTagSymbol = env->GetToStringTagSymbol(); + JSHandle stringTag(JSObject::GetProperty(thread, set, toStringTagSymbol).GetValue()); + JSHandle str = factory->NewFromString("Set"); + EXPECT_TRUE(!stringTag.GetTaggedValue().IsUndefined()); + EXPECT_TRUE(EcmaString::StringsAreEqual(*str, *stringTag)); + + JSHandle constructor = JSHandle::Cast(JSTaggedValue::ToObject(thread, valueHandle)); + EXPECT_EQ(JSHandle(set)->GetPrototype(thread), constructor->GetFunctionPrototype()); + + JSHandle key1(factory->NewFromString("add")); + JSTaggedValue value1 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value1.IsUndefined()); + + JSHandle key2(factory->NewFromString("has")); + JSTaggedValue value2 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value2.IsUndefined()); + + JSHandle key3(factory->NewFromString("clear")); + JSTaggedValue value3 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value3.IsUndefined()); + + JSHandle key4(factory->NewFromString("size")); + JSTaggedValue value4 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value4.IsUndefined()); + + JSHandle key5(factory->NewFromString("delete")); + JSTaggedValue value5 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value5.IsUndefined()); + + JSHandle key6(factory->NewFromString("forEach")); + JSTaggedValue value6 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value6.IsUndefined()); +} + +HWTEST_F_L0(BuiltinsSetTest, GetIterator) +{ + JSHandle set(thread, CreateBuiltinsSet(thread)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + // test Values() + JSTaggedValue result = BuiltinsSet::Values(ecmaRuntimeCallInfo.get()); + JSHandle iter(thread, result); + EXPECT_TRUE(iter->IsJSSetIterator()); + EXPECT_EQ(IterationKind::VALUE, IterationKind(iter->GetIterationKind().GetInt())); + EXPECT_EQ(JSSet::Cast(set.GetTaggedValue().GetTaggedObject())->GetLinkedSet(), iter->GetIteratedSet()); + + // test entrys() + JSTaggedValue result2 = BuiltinsSet::Entries(ecmaRuntimeCallInfo.get()); + JSHandle iter2(thread, result2); + EXPECT_TRUE(iter2->IsJSSetIterator()); + EXPECT_EQ(IterationKind::KEY_AND_VALUE, IterationKind(iter2->GetIterationKind().GetInt())); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_string_test.cpp b/ecmascript/builtins/tests/builtins_string_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e33586e28f5f0e1e9252429dd66cc4e7e52db89a --- /dev/null +++ b/ecmascript/builtins/tests/builtins_string_test.cpp @@ -0,0 +1,1217 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/builtins/builtins_regexp.h" +#include "ecmascript/builtins/builtins_string.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" + +#include "ecmascript/js_array.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_regexp.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsStringTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + JSTaggedValue CreateRegExpObjByPatternAndFlags(JSThread *thread, const JSHandle &pattern, + const JSHandle &flags) + { + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle regexp(env->GetRegExpFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + // 8 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, regexp.GetTaggedValue(), 8); + ecmaRuntimeCallInfo->SetFunction(regexp.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, pattern.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, flags.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsRegExp::RegExpConstructor(ecmaRuntimeCallInfo.get()); + return result; + } +}; + +HWTEST_F_L0(BuiltinsStringTest, StringConstructor1) +{ + ASSERT_NE(thread, nullptr); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle string(env->GetStringFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + JSHandle string2 = factory->NewFromString("ABC"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, string.GetTaggedValue(), 6); + ecmaRuntimeCallInfo->SetFunction(string.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, string2.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::StringConstructor(ecmaRuntimeCallInfo.get()); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + JSHandle ref(thread, JSPrimitiveRef::Cast(value.GetTaggedObject())); + JSTaggedValue test = factory->NewFromString("ABC").GetTaggedValue(); + ASSERT_EQ( + EcmaString::Cast(ref->GetValue().GetTaggedObject())->Compare(reinterpret_cast(test.GetRawData())), + 0); +} + +// String.fromCharCode(65, 66, 67) +HWTEST_F_L0(BuiltinsStringTest, fromCharCode1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const double arg1 = 65; + const double arg2 = 66; + const double arg3 = 67; + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(arg1)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(arg2)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(arg3)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::FromCharCode(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSTaggedValue value(static_cast(result.GetRawData())); + JSHandle valueHandle(thread, JSTaggedValue(value.GetTaggedObject())); + JSTaggedValue test = factory->NewFromString("ABC").GetTaggedValue(); + ASSERT_EQ( + EcmaString::Cast(valueHandle->GetTaggedObject())->Compare(reinterpret_cast(test.GetRawData())), + 0); +} + +// String.fromCodePoint(65, 66, 67) +HWTEST_F_L0(BuiltinsStringTest, fromCodePoint1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const double arg1 = 65; + const double arg2 = 66; + const double arg3 = 67; + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(arg1)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(arg2)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(arg3)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::FromCodePoint(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("ABC").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "abcabcabc".charAt(5) +HWTEST_F_L0(BuiltinsStringTest, charAt1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisVal = factory->NewFromString("abcabcabc"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisVal.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::CharAt(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("c").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "一二三四".charAt(2) +HWTEST_F_L0(BuiltinsStringTest, charAt2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisVal = factory->NewFromString("一二三四"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisVal.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::CharAt(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("三").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "abcabcabc".charAt(-1) +HWTEST_F_L0(BuiltinsStringTest, charAt3) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisVal = factory->NewFromString("abcabcabc"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisVal.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::CharAt(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->GetEmptyString().GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "ABC".charCodeAt(0) +HWTEST_F_L0(BuiltinsStringTest, charCodeAt1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisVal = factory->NewFromString("ABC"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisVal.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::CharCodeAt(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(65).GetRawData()); +} + +// "ABC".charCodeAt(-1) +HWTEST_F_L0(BuiltinsStringTest, charCodeAt2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisVal = factory->NewFromString("ABC"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisVal.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::CharCodeAt(ecmaRuntimeCallInfo.get()); + + JSTaggedValue test = BuiltinsString::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), test.GetRawData()); +} + +// "ABC".codePointAt(1) +HWTEST_F_L0(BuiltinsStringTest, codePointAt1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisVal = factory->NewFromString("ABC"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisVal.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::CodePointAt(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(66).GetRawData()); +} + +// 'a'.concat('b', 'c', 'd') +HWTEST_F_L0(BuiltinsStringTest, concat1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("a"); + JSHandle val1 = factory->NewFromString("b"); + JSHandle val2 = factory->NewFromString("c"); + JSHandle val3 = factory->NewFromString("d"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val1.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, val2.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, val3.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Concat(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("abcd").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "abcabcabc".indexof('b') +HWTEST_F_L0(BuiltinsStringTest, indexof1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abcabcabc"); + JSHandle val = factory->NewFromString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::IndexOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(1).GetRawData()); +} + +// "abcabcabc".indexof('b', 2) +HWTEST_F_L0(BuiltinsStringTest, indexof2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abcabcabc"); + JSHandle val = factory->NewFromString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::IndexOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(4).GetRawData()); +} + +// "abcabcabc".indexof('d') +HWTEST_F_L0(BuiltinsStringTest, indexof3) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abcabcabc"); + JSHandle val = factory->NewFromString("d"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::IndexOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); +} + +// "abcabcabc".lastIndexOf('b') +HWTEST_F_L0(BuiltinsStringTest, lastIndexOf1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abcabcabc"); + JSHandle val = factory->NewFromString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::LastIndexOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(7).GetRawData()); +} +// "abcabcabc".lastIndexOf('b', 2) +HWTEST_F_L0(BuiltinsStringTest, lastIndexOf2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abcabcabc"); + JSHandle val = factory->NewFromString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::LastIndexOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(1).GetRawData()); +} + +// "abcabcabc".lastIndexOf('d') +HWTEST_F_L0(BuiltinsStringTest, lastIndexOf3) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abcabcabc"); + JSHandle val = factory->NewFromString("d"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::LastIndexOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); +} + +// "abcabcabc".includes('b') +HWTEST_F_L0(BuiltinsStringTest, Includes2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abcabcabc"); + JSHandle val = factory->NewFromString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Includes(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// "abccccccc".includes('b',2) +HWTEST_F_L0(BuiltinsStringTest, Includes3) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abccccccc"); + JSHandle val = factory->NewFromString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Includes(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// "一二三四".includes('二') +HWTEST_F_L0(BuiltinsStringTest, Includes4) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("一二三四"); + JSHandle val = factory->NewFromString("二"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Includes(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// "To be, or not to be, that is the question.".startsWith('To be') +HWTEST_F_L0(BuiltinsStringTest, startsWith1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("To be, or not to be, that is the question."); + JSHandle val = factory->NewFromString("To be"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::StartsWith(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// "To be, or not to be, that is the question.".startsWith('not to be') +HWTEST_F_L0(BuiltinsStringTest, startsWith2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("To be, or not to be, that is the question."); + JSHandle val = factory->NewFromString("not to be"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::StartsWith(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// "To be, or not to be, that is the question.".startsWith('not to be', 10) +HWTEST_F_L0(BuiltinsStringTest, startsWith3) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("To be, or not to be, that is the question."); + JSHandle val = factory->NewFromString("not to be"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::StartsWith(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// "To be, or not to be, that is the question.".endsWith('question.') +HWTEST_F_L0(BuiltinsStringTest, endsWith1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("To be, or not to be, that is the question."); + JSHandle val = factory->NewFromString("question."); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::EndsWith(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// "To be, or not to be, that is the question.".endsWith('to be') +HWTEST_F_L0(BuiltinsStringTest, endsWith2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("To be, or not to be, that is the question."); + JSHandle val = factory->NewFromString("to be"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::EndsWith(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// "To be, or not to be, that is the question.".endsWith('to be', 19) +HWTEST_F_L0(BuiltinsStringTest, endsWith3) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("To be, or not to be, that is the question."); + JSHandle val = factory->NewFromString("to be"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(19))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::EndsWith(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// "ABC".toLowerCase() +HWTEST_F_L0(BuiltinsStringTest, toLowerCase1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("ABC"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::ToLowerCase(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSHandle test = factory->NewFromString("abc"); + ASSERT_TRUE(JSTaggedValue::SameValue(resultHandle.GetTaggedValue(), test.GetTaggedValue())); +} + +// "abc".toUpperCase() +HWTEST_F_L0(BuiltinsStringTest, toUpperCase1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abc"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::ToUpperCase(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("ABC").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "abc".localecompare('b') +HWTEST_F_L0(BuiltinsStringTest, localecompare1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abc"); + JSHandle val = factory->NewFromString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::LocaleCompare(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); +} + +// "abc".localecompare('abc') +HWTEST_F_L0(BuiltinsStringTest, localecompare2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abc"); + JSHandle val = factory->NewFromString("abc"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::LocaleCompare(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(0).GetRawData()); +} + +// "abc".localecompare('aa') +HWTEST_F_L0(BuiltinsStringTest, localecompare3) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abc"); + JSHandle val = factory->NewFromString("aa"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::LocaleCompare(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(1).GetRawData()); +} + +// "abc".repeat(5) +HWTEST_F_L0(BuiltinsStringTest, repeat1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abc"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Repeat(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("abcabcabcabcabc").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// 'The morning is upon us.'.slice(4, -2) +HWTEST_F_L0(BuiltinsStringTest, slice1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("The morning is upon us."); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(4))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Slice(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("morning is upon u").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// 'The morning is upon us.'.slice(12) +HWTEST_F_L0(BuiltinsStringTest, slice2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("The morning is upon us."); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(12))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Slice(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("is upon us.").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// 'Mozilla'.substring(3, -3) +HWTEST_F_L0(BuiltinsStringTest, substring1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("Mozilla"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Substring(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("Moz").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// 'Mozilla'.substring(7, 4) +HWTEST_F_L0(BuiltinsStringTest, substring2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("Mozilla"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(7))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(4))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Substring(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("lla").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// " Hello world! ".trim() +HWTEST_F_L0(BuiltinsStringTest, trim1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString(" Hello world! "); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Trim(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("Hello world!").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +HWTEST_F_L0(BuiltinsStringTest, trim2) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString(" Hello world! "); + JSHandle stringObject(env->GetStringFunction()); + JSHandle value(thread, JSTaggedValue(thisStr.GetTaggedValue().GetTaggedObject())); + JSHandle str = factory->NewJSPrimitiveRef(stringObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Trim(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("Hello world!").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +HWTEST_F_L0(BuiltinsStringTest, ToString) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abcabcabc"); + JSHandle stringObject(env->GetStringFunction()); + JSHandle value(thread, JSTaggedValue(thisStr.GetTaggedValue().GetTaggedObject())); + JSHandle str = factory->NewJSPrimitiveRef(stringObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::ToString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = JSTaggedValue(*thisStr); + ASSERT_EQ(result.GetRawData(), test.GetRawData()); +} + +HWTEST_F_L0(BuiltinsStringTest, ValueOf) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("abcabcabc"); + JSHandle stringObject(env->GetStringFunction()); + JSHandle value(thread, JSTaggedValue(thisStr.GetTaggedValue().GetTaggedObject())); + JSHandle str = factory->NewJSPrimitiveRef(stringObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::ValueOf(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = JSTaggedValue(*thisStr); + ASSERT_EQ(result.GetRawData(), test.GetRawData()); +} + +static inline JSFunction *BuiltinsStringTestCreate(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + return globalEnv->GetObjectFunction().GetObject(); +} + +HWTEST_F_L0(BuiltinsStringTest, Raw) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle foo(factory->NewFromString("foo")); + JSHandle bar(factory->NewFromString("bar")); + JSHandle baz(factory->NewFromString("baz")); + JSHandle rawArray = JSHandle::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle obj(rawArray); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, foo); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, bar); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, baz); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle constructor(thread, BuiltinsStringTestCreate(thread)); + JSHandle templateString( + factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + JSHandle rawKey(factory->NewFromString("raw")); + JSObject::SetProperty(thread, templateString, rawKey, rawArray); + JSHandle test = factory->NewFromString("foo5barJavaScriptbaz"); + + JSHandle javascript = factory->NewFromString("JavaScript"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(templateString.GetObject())); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo->SetCallArg(2, javascript.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Raw(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *test)); +} + +HWTEST_F_L0(BuiltinsStringTest, Replace) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("Twas the night before Xmas..."); + JSHandle searchStr = factory->NewFromString("Xmas"); + JSHandle replaceStr = factory->NewFromString("Christmas"); + JSHandle expected = factory->NewFromString("Twas the night before Christmas..."); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Replace(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expected)); + + JSHandle replaceStr1 = factory->NewFromString("abc$$"); + JSHandle expected1 = factory->NewFromString("Twas the night before abc$..."); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, replaceStr1.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsString::Replace(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle resultString1(thread, result1); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString1, *expected1)); + + JSHandle replaceStr2 = factory->NewFromString("abc$$dd"); + JSHandle expected2 = factory->NewFromString("Twas the night before abc$dd..."); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(1, replaceStr2.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsString::Replace(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle resultString2(thread, result2); + ASSERT_TRUE(result2.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString2, *expected2)); + + JSHandle replaceStr3 = factory->NewFromString("abc$&dd"); + JSHandle expected3 = factory->NewFromString("Twas the night before abcXmasdd..."); + + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(1, replaceStr3.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + JSTaggedValue result3 = BuiltinsString::Replace(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle resultString3(thread, result3); + ASSERT_TRUE(result3.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString3, *expected3)); + + JSHandle replaceStr4 = factory->NewFromString("abc$`dd"); + JSHandle expected4 = factory->NewFromString("Twas the night before abcTwas the night before dd..."); + + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(1, replaceStr4.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue result4 = BuiltinsString::Replace(ecmaRuntimeCallInfo4.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle resultString4(thread, result4); + ASSERT_TRUE(result4.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString4, *expected4)); +} + +HWTEST_F_L0(BuiltinsStringTest, Replace2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("Twas the night before Xmas..."); + JSHandle searchStr = factory->NewFromString("Xmas"); + JSHandle replaceStr = factory->NewFromString("abc$\'dd"); + JSHandle expected = factory->NewFromString("Twas the night before abc...dd..."); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Replace(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expected)); + + JSHandle replaceStr2 = factory->NewFromString("abc$`dd$\'$ff"); + JSHandle expected2 = + factory->NewFromString("Twas the night before abcTwas the night before dd...$ff..."); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(1, replaceStr2.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsString::Replace(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle resultString2(thread, result2); + ASSERT_TRUE(result2.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString2, *expected2)); + + JSHandle replaceStr3 = factory->NewFromString("abc$`dd$\'$"); + JSHandle expected3 = factory->NewFromString("Twas the night before abcTwas the night before dd...$..."); + + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(1, replaceStr3.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + JSTaggedValue result3 = BuiltinsString::Replace(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle resultString3(thread, result3); + ASSERT_TRUE(result3.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString3, *expected3)); + + JSHandle replaceStr4 = factory->NewFromString("abc$`dd$$"); + JSHandle expected4 = factory->NewFromString("Twas the night before abcTwas the night before dd$..."); + + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(1, replaceStr4.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue result4 = BuiltinsString::Replace(ecmaRuntimeCallInfo4.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result4.IsString()); + JSHandle resultString4(thread, result4); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString4, *expected4)); +} + +HWTEST_F_L0(BuiltinsStringTest, Replace3) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("Twas the night before Xmas..."); + JSHandle searchStr = factory->NewFromString("Xmas"); + JSHandle replaceStr = factory->NewFromString("$&a $` $\' $2 $01 $$1 $21 $32 a"); + JSHandle expected = + factory->NewFromString("Twas the night before Xmasa Twas the night before ... $2 $01 $1 $21 $32 a..."); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Replace(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expected)); +} + +HWTEST_F_L0(BuiltinsStringTest, Replace4) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromString("quick\\s(brown).+?(jumps)"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle searchStr(thread, reinterpret_cast(result1.GetRawData())); + JSHandle expected = thread->GetEcmaVM()->GetFactory()->NewFromString( + "The Quick Brown Fox Jumpsa The Over The Lazy Dog Jumps Brown $1 Jumps1 $32 a Over The Lazy Dog"); + + // make dyn_runtime_call_info2 + JSHandle thisStr = + thread->GetEcmaVM()->GetFactory()->NewFromString("The Quick Brown Fox Jumps Over The Lazy Dog"); + JSHandle replaceStr = + thread->GetEcmaVM()->GetFactory()->NewFromString("$&a $` $\' $2 $01 $$1 $21 $32 a"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Replace(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expected)); +} + +HWTEST_F_L0(BuiltinsStringTest, Split) +{ + // invoke RegExpConstructor method + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("Hello World. How are you doing?"); + JSHandle separatorStr = factory->NewFromString(" "); + JSHandle limit(thread, JSTaggedValue(3)); + JSHandle expected1 = factory->NewFromString("Hello"); + JSHandle expected2 = factory->NewFromString("World."); + JSHandle expected3 = factory->NewFromString("How"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, separatorStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Split(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle resultArray(thread, reinterpret_cast(result.GetRawData())); + ASSERT_TRUE(resultArray->IsJSArray()); + JSHandle resultObj(resultArray); + JSHandle string1( + JSObject::GetProperty(thread, resultObj, JSHandle(thread, JSTaggedValue(0))).GetValue()); + JSHandle string2( + JSObject::GetProperty(thread, resultObj, JSHandle(thread, JSTaggedValue(1))).GetValue()); + JSHandle string3( + JSObject::GetProperty(thread, resultObj, JSHandle(thread, JSTaggedValue(2))).GetValue()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*string1, *expected1)); + ASSERT_TRUE(EcmaString::StringsAreEqual(*string2, *expected2)); + ASSERT_TRUE(EcmaString::StringsAreEqual(*string3, *expected3)); +} + +HWTEST_F_L0(BuiltinsStringTest, Split2) +{ + // invoke RegExpConstructor method + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("a-b-c"); + JSHandle pattern1 = factory->NewFromString("-"); + JSHandle flags1 = factory->NewFromString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle separatorObj(thread, result1); + + JSHandle limit(thread, JSTaggedValue(3)); + JSHandle expected1 = factory->NewFromString("a"); + JSHandle expected2 = factory->NewFromString("b"); + JSHandle expected3 = factory->NewFromString("c"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, separatorObj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Split(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle resultArray(thread, result); + ASSERT_TRUE(resultArray->IsJSArray()); + JSHandle resultObj(resultArray); + JSHandle string1( + JSObject::GetProperty(thread, resultObj, JSHandle(thread, JSTaggedValue(0))).GetValue()); + JSHandle string2( + JSObject::GetProperty(thread, resultObj, JSHandle(thread, JSTaggedValue(1))).GetValue()); + JSHandle string3( + JSObject::GetProperty(thread, resultObj, JSHandle(thread, JSTaggedValue(2))).GetValue()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*string1, *expected1)); + ASSERT_TRUE(EcmaString::StringsAreEqual(*string2, *expected2)); + ASSERT_TRUE(EcmaString::StringsAreEqual(*string3, *expected3)); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_symbol_test.cpp b/ecmascript/builtins/tests/builtins_symbol_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..344eb43bf9d03d7ae91504d6db47dcf5b8ab2047 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_symbol_test.cpp @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_symbol.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/symbol_table-inl.h" +#include "ecmascript/tests/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using Symbol = ecmascript::builtins::BuiltinsSymbol; +using BuiltinsBase = panda::ecmascript::base::BuiltinsBase; + +class BuiltinsSymbolTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +// new Symbol.toString() +HWTEST_F_L0(BuiltinsSymbolTest, SymbolNoParameterToString) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + + JSHandle symbol = ecmaVM->GetFactory()->NewJSSymbol(); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Symbol::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + ASSERT_TRUE(result.IsString()); + + auto symbolValue = ecmascript::base::BuiltinsBase::GetTaggedString(thread, "Symbol()"); + ASSERT_EQ(reinterpret_cast(symbolValue.GetRawData())->Compare(*resultHandle), 0); +} + +// new Symbol("aaa").toString() +HWTEST_F_L0(BuiltinsSymbolTest, SymbolWithParameterToString) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + + JSHandle symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("aaa"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Symbol::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + ASSERT_TRUE(result.IsString()); + + auto symbolValue = ecmascript::base::BuiltinsBase::GetTaggedString(thread, "Symbol(aaa)"); + ASSERT_EQ(reinterpret_cast(symbolValue.GetRawData())->Compare(*resultHandle), 0); +} + +// new Symbol().valueOf() +HWTEST_F_L0(BuiltinsSymbolTest, SymbolNoParameterValueOf) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle symbol = ecmaVM->GetFactory()->NewJSSymbol(); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::ValueOf(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsSymbol()); + ASSERT_EQ(result.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true); + + JSHandle symbolObject(env->GetSymbolFunction()); + JSHandle symbolValue(symbol); + JSHandle symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue); + + auto otherEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + otherEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + otherEcmaRuntimeCallInfo->SetThis(symbolRef.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, otherEcmaRuntimeCallInfo.get()); + JSTaggedValue otherResult = BuiltinsSymbol::ValueOf(otherEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(otherResult.IsSymbol()); + ASSERT_EQ(otherResult.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true); +} + +// new Symbol("bbb").valueOf() +HWTEST_F_L0(BuiltinsSymbolTest, SymbolWithParameterValueOf) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("bbb"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::ValueOf(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsSymbol()); + ASSERT_EQ(result.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true); + + JSHandle symbolObject(env->GetSymbolFunction()); + JSHandle symbolValue(symbol); + JSHandle symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue); + + auto otherEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + otherEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + otherEcmaRuntimeCallInfo->SetThis(symbolRef.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, otherEcmaRuntimeCallInfo.get()); + JSTaggedValue otherResult = BuiltinsSymbol::ValueOf(otherEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(otherResult.IsSymbol()); + ASSERT_EQ(otherResult.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true); +} + +// new Symbol().for +HWTEST_F_L0(BuiltinsSymbolTest, SymbolWithParameterFor) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle tableHandle(env->GetRegisterSymbols()); + + JSHandle string = ecmaVM->GetFactory()->NewFromString("ccc"); + ASSERT_EQ(string->GetLength(), 3); + JSHandle string_handle(string); + ASSERT_EQ(tableHandle->ContainsKey(thread, string_handle.GetTaggedValue()), false); + + JSHandle symbol = ecmaVM->GetFactory()->NewSymbolWithTableWithChar("ccc"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, string.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::For(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(tableHandle->ContainsKey(thread, string_handle.GetTaggedValue()), true); + + JSTaggedValue target(*symbol); + ASSERT_EQ(result.GetRawData() == target.GetRawData(), true); +} + +// Symbol.keyFor (sym) +HWTEST_F_L0(BuiltinsSymbolTest, SymbolKeyFor) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("bbb"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::KeyFor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + JSHandle string = ecmaVM->GetFactory()->NewFromString("ccc"); + ASSERT_EQ(string->GetLength(), 3); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, string.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + BuiltinsSymbol::For(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle otherSymbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("ccc"); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetCallArg(0, otherSymbol.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue otherResult = BuiltinsSymbol::KeyFor(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(otherResult.IsString()); + JSHandle tableHandle(env->GetRegisterSymbols()); + JSTaggedValue stringValue(*string); + ASSERT_EQ(tableHandle->ContainsKey(thread, stringValue), true); +} + +// Symbol.ToPrimitive() +HWTEST_F_L0(BuiltinsSymbolTest, SymbolToPrimitive) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle symbol = ecmaVM->GetFactory()->NewJSSymbol(); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::ToPrimitive(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsSymbol()); + ASSERT_EQ(result.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true); + + JSHandle symbolObject(env->GetSymbolFunction()); + JSHandle symbolValue(symbol); + JSHandle symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue); + + auto otherEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + otherEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + otherEcmaRuntimeCallInfo->SetThis(symbolRef.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, otherEcmaRuntimeCallInfo.get()); + JSTaggedValue otherResult = BuiltinsSymbol::ToPrimitive(otherEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(otherResult.IsSymbol()); + ASSERT_EQ(otherResult.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true); +} + +// constructor +HWTEST_F_L0(BuiltinsSymbolTest, SymbolConstructor) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::SymbolConstructor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsSymbol()); + JSSymbol *sym = reinterpret_cast(result.GetRawData()); + ASSERT_EQ(sym->GetDescription().IsUndefined(), true); + + JSHandle string = ecmaVM->GetFactory()->NewFromString("ddd"); + + auto otherEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + otherEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + otherEcmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + otherEcmaRuntimeCallInfo->SetCallArg(0, string.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, otherEcmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsSymbol::SymbolConstructor(otherEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + JSHandle resultString = JSTaggedValue::ToString( + thread, JSHandle(thread, reinterpret_cast(result1.GetRawData())->GetDescription())); + ASSERT_EQ(resultString->Compare(*string), 0); +} + +HWTEST_F_L0(BuiltinsSymbolTest, SymbolGetter) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + + JSHandle symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar(""); + JSHandle string = ecmaVM->GetFactory()->NewFromString(""); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::DescriptionGetter(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result.IsString()); + EcmaString *resString = reinterpret_cast(result.GetRawData()); + ASSERT_EQ(resString->GetLength(), 0); + ASSERT_EQ(EcmaString::StringsAreEqual(resString, *string), true); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_typedarray_test.cpp b/ecmascript/builtins/tests/builtins_typedarray_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4b13b2c4358a6aa0ca5852f8cd9b27a30715f242 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_typedarray_test.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/base/typed_array_helper.h" + +#include "ecmascript/builtins/builtins_array.h" +#include "ecmascript/builtins/builtins_object.h" +#include "ecmascript/builtins/builtins_typedarray.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_int8_array.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/object_operator.h" + +#include "ecmascript/tests/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +using namespace panda::ecmascript::base; + +namespace panda::test { +using Array = ecmascript::builtins::BuiltinsArray; +using TypedArray = ecmascript::builtins::BuiltinsTypedArray; +using TypedArrayHelper = ecmascript::base::TypedArrayHelper; + +class BuiltinsTypedArrayTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + +protected: + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv) + { + JSHandle key = GetCallArg(argv, 0); + if (key->IsUndefined()) { + return JSTaggedValue::Undefined(); + } + JSArray *jsArray = JSArray::Cast(GetThis(argv)->GetTaggedObject()); + int length = jsArray->GetArrayLength() + 1; + jsArray->SetArrayLength(argv->GetThread(), length); + return JSTaggedValue::Undefined(); + } + + static JSTaggedValue TestEveryFunc(EcmaRuntimeCallInfo *argv) + { + array_size_t argc = argv->GetArgsNumber(); + if (argc > 0) { + [[maybe_unused]] int aaa = GetCallArg(argv, 0)->GetInt(); + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestFilterFunc(EcmaRuntimeCallInfo *argv) + { + ASSERT(argv); + array_size_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestMapFunc(EcmaRuntimeCallInfo *argv) + { + int accumulator = GetCallArg(argv, 0)->GetInt(); + accumulator = accumulator * 2; // 2 : mapped to 2 times the original value + return BuiltinsBase::GetTaggedInt(accumulator); + } + + static JSTaggedValue TestFindFunc(EcmaRuntimeCallInfo *argv) + { + array_size_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestFindIndexFunc(EcmaRuntimeCallInfo *argv) + { + array_size_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestReduceFunc(EcmaRuntimeCallInfo *argv) + { + int accumulator = GetCallArg(argv, 0)->GetInt(); + accumulator = accumulator + GetCallArg(argv, 1)->GetInt(); + return BuiltinsBase::GetTaggedInt(accumulator); + } + + static JSTaggedValue TestReduceRightFunc(EcmaRuntimeCallInfo *argv) + { + int accumulator = GetCallArg(argv, 0)->GetInt(); + accumulator = accumulator + GetCallArg(argv, 1)->GetInt(); + return BuiltinsBase::GetTaggedInt(accumulator); + } + + static JSTaggedValue TestSomeFunc(EcmaRuntimeCallInfo *argv) + { + array_size_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + }; +}; + +JSTaggedValue CreateBuiltinsJSObject(JSThread *thread, const CString keyCStr) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle dynclass = env->GetObjectFunction(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + JSHandle key(factory->NewFromString(&keyCStr[0])); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, obj, key, value); + return obj.GetTaggedValue(); +} + +JSInt8Array *CreateTypedArrayFromList(JSThread *thread, const JSHandle &array) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle jsarray(JSArray::CreateArrayFromList(thread, array)); + JSHandle int8_array(env->GetInt8ArrayFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + // 6 : test case + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetNewTarget(JSTaggedValue(*int8_array)); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(*globalObject)); + ecmaRuntimeCallInfo1->SetCallArg(0, jsarray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = TypedArray::Int8ArrayConstructor(ecmaRuntimeCallInfo1.get()); + + EXPECT_TRUE(result.IsECMAObject()); + JSInt8Array *int8arr = JSInt8Array::Cast(reinterpret_cast(result.GetRawData())); + return int8arr; +} + + +HWTEST_F_L0(BuiltinsTypedArrayTest, Species) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle array(env->GetArrayFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(array.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(globalObject.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = TypedArray::Species(ecmaRuntimeCallInfo1.get()); + ASSERT_TRUE(result.IsECMAObject()); +} +} // namespace panda::test \ No newline at end of file diff --git a/ecmascript/builtins/tests/builtins_weak_map_test.cpp b/ecmascript/builtins/tests/builtins_weak_map_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d745d0fabf65aac8178c4b87af388c605221f0d9 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_weak_map_test.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/builtins/builtins_weak_map.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_map_iterator.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/js_weak_container.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tests/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using BuiltinsWeakMap = ecmascript::builtins::BuiltinsWeakMap; +using JSWeakMap = ecmascript::JSWeakMap; + +class BuiltinsWeakMapTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +static JSObject *JSObjectTestCreate(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVM->GetFactory(); + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle jsFunc = globalEnv->GetObjectFunction(); + return *factory->NewJSObjectByConstructor(JSHandle(jsFunc), jsFunc); +} + +JSWeakMap *CreateBuiltinsWeakMap(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsWeakMapFunction()); + // 4 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, newTarget.GetTaggedValue(), 4); + ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsWeakMap::WeakMapConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + return JSWeakMap::Cast(reinterpret_cast(result.GetRawData())); +} + +// new Map("abrupt").toString() +HWTEST_F_L0(BuiltinsWeakMapTest, CreateAndGetSize) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsWeakMapFunction()); + JSHandle map(thread, CreateBuiltinsWeakMap(thread)); + + JSHandle array(factory->NewTaggedArray(1)); + JSHandle internal_array(factory->NewTaggedArray(2)); + JSTaggedValue value(JSObjectTestCreate(thread)); + internal_array->Set(thread, 0, value); + internal_array->Set(thread, 1, JSTaggedValue(0)); + auto result = JSArray::CreateArrayFromList(thread, internal_array); + array->Set(thread, 0, result); + + JSHandle values = JSArray::CreateArrayFromList(thread, array); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, values.GetTaggedValue()); + ecmaRuntimeCallInfo->SetNewTarget(newTarget.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + JSTaggedValue result1 = BuiltinsWeakMap::WeakMapConstructor(ecmaRuntimeCallInfo.get()); + JSHandle weakMap(thread, JSWeakMap::Cast(reinterpret_cast(result1.GetRawData()))); + EXPECT_EQ(weakMap->GetSize(), 1); +} + +HWTEST_F_L0(BuiltinsWeakMapTest, SetAndHas) +{ + // create jsWeakMap + JSHandle weakMap(thread, CreateBuiltinsWeakMap(thread)); + JSHandle key(thread, JSObjectTestCreate(thread)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(weakMap.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo.get()); + + EXPECT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData()); + } + + // test Set() + JSTaggedValue result2 = BuiltinsWeakMap::Set(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result2.IsECMAObject()); + JSWeakMap *jsWeakMap = JSWeakMap::Cast(reinterpret_cast(result2.GetRawData())); + EXPECT_EQ(jsWeakMap->GetSize(), 1); + + // test Has() + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(jsWeakMap)); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result3 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + } +} + +HWTEST_F_L0(BuiltinsWeakMapTest, DeleteAndRemove) +{ + // create jsWeakMap + JSHandle weakMap(thread, CreateBuiltinsWeakMap(thread)); + + // add 40 keys + JSTaggedValue lastKey(JSTaggedValue::Undefined()); + for (int i = 0; i < 40; i++) { + JSHandle key(thread, JSObjectTestCreate(thread)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(weakMap.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(i))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsWeakMap::Set(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result1.IsECMAObject()); + JSWeakMap *jsWeakMap = JSWeakMap::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsWeakMap->GetSize(), i + 1); + lastKey = key.GetTaggedValue(); + } + + // whether jsWeakMap has delete lastKey + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(weakMap.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, lastKey); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); + + // delete + JSTaggedValue result3 = BuiltinsWeakMap::Delete(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + + // check deleteKey is deleted + JSTaggedValue result4 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); +} +} // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_weak_set_test.cpp b/ecmascript/builtins/tests/builtins_weak_set_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..469170b46dfd24018322de79c862053e3830ac54 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_weak_set_test.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_weak_set.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" + +#include "ecmascript/js_weak_container.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using BuiltinsWeakSet = ecmascript::builtins::BuiltinsWeakSet; +using JSWeakSet = ecmascript::JSWeakSet; + +class BuiltinsWeakSetTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +static JSObject *JSObjectTestCreate(JSThread *thread) +{ + [[maybe_unused]] EcmaHandleScope scope(thread); + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle jsFunc = globalEnv->GetObjectFunction(); + JSHandle newObj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(jsFunc), jsFunc); + return *newObj; +} + +JSWeakSet *CreateBuiltinsWeakSet(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsWeakSetFunction()); + + // 4 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 4); + ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsWeakSet::WeakSetConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + JSWeakSet *jsWeakSet = JSWeakSet::Cast(reinterpret_cast(result.GetRawData())); + return jsWeakSet; +} + +HWTEST_F_L0(BuiltinsWeakSetTest, CreateAndGetSize) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsWeakSetFunction()); + JSHandle weakSet(thread, CreateBuiltinsWeakSet(thread)); + + JSHandle array(factory->NewTaggedArray(5)); + for (int i = 0; i < 5; i++) { + JSHandle key(thread, JSObjectTestCreate(thread)); + array->Set(thread, i, key.GetTaggedValue()); + } + + JSHandle values = JSArray::CreateArrayFromList(thread, array); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(weakSet.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, values.GetTaggedValue()); + ecmaRuntimeCallInfo->SetNewTarget(newTarget.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + JSTaggedValue result1 = BuiltinsWeakSet::WeakSetConstructor(ecmaRuntimeCallInfo.get()); + JSHandle weakSetResult(thread, + JSWeakSet::Cast(reinterpret_cast(result1.GetRawData()))); + EXPECT_EQ(weakSetResult->GetSize(), 5); +} + +HWTEST_F_L0(BuiltinsWeakSetTest, AddAndHas) +{ + // create jsWeakSet + JSHandle weakSet(thread, CreateBuiltinsWeakSet(thread)); + JSHandle key(thread, JSObjectTestCreate(thread)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(weakSet.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + JSWeakSet *jsWeakSet; + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsWeakSet::Has(ecmaRuntimeCallInfo.get()); + + EXPECT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData()); + + // test Add() + JSTaggedValue result2 = BuiltinsWeakSet::Add(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result2.IsECMAObject()); + jsWeakSet = JSWeakSet::Cast(reinterpret_cast(result2.GetRawData())); + EXPECT_EQ(jsWeakSet->GetSize(), 1); + } + + // test Has() + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(jsWeakSet)); + ecmaRuntimeCallInfo1->SetCallArg(0, key.GetTaggedValue()); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result3 = BuiltinsWeakSet::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + } +} + +HWTEST_F_L0(BuiltinsWeakSetTest, DeleteAndRemove) +{ + // create jsSet + JSHandle weakSet(thread, CreateBuiltinsWeakSet(thread)); + + // add 40 keys + JSTaggedValue lastKey(JSTaggedValue::Undefined()); + for (int i = 0; i < 40; i++) { + JSHandle key(thread, JSObjectTestCreate(thread)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(weakSet.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsWeakSet::Add(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result1.IsECMAObject()); + JSWeakSet *jsWeakSet = JSWeakSet::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsWeakSet->GetSize(), i + 1); + lastKey = key.GetTaggedValue(); + } + // whether jsWeakSet has delete lastKey + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(weakSet.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, lastKey); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsWeakSet::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); + + // delete + JSTaggedValue result3 = BuiltinsWeakSet::Delete(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + + // check deleteKey is deleted + JSTaggedValue result4 = BuiltinsWeakSet::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); +} +} // namespace panda::test diff --git a/ecmascript/class_linker/panda_file_translator.cpp b/ecmascript/class_linker/panda_file_translator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1e44b3f1e4d97a5fab604d2a67285ba8f70e48f7 --- /dev/null +++ b/ecmascript/class_linker/panda_file_translator.cpp @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "panda_file_translator.h" + +#include +#include +#include + +#include "ecmascript/class_linker/program_object-inl.h" +#include "ecmascript/global_env.h" +#include "ecmascript/interpreter/interpreter.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/literal_data_extractor.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array.h" +#include "libpandabase/mem/mem.h" +#include "libpandabase/utils/logger.h" +#include "libpandabase/utils/utf.h" +#include "libpandafile/bytecode_instruction-inl.h" +#include "libpandafile/class_data_accessor-inl.h" + +namespace panda::ecmascript { +PandaFileTranslator::PandaFileTranslator(EcmaVM *vm) + : ecmaVm_(vm), factory_(vm->GetFactory()), thread_(vm->GetJSThread()) +{ +} + +JSHandle PandaFileTranslator::TranslatePandaFile(EcmaVM *vm, const panda_file::File &pf, + const CString &methodName) +{ + PandaFileTranslator translator(vm); + translator.TranslateClasses(pf, methodName); + auto result = translator.GenerateProgram(pf); + return JSHandle(translator.thread_, result); +} + +template +static T *InitializeMemory(T *mem, Args... args) +{ + return new (mem) T(std::forward(args)...); +} + +const JSMethod *PandaFileTranslator::FindMethods(uint32_t offset) const +{ + Span methods = GetMethods(); + auto pred = [offset](const JSMethod &method) { return method.GetFileId().GetOffset() == offset; }; + auto it = std::find_if(methods.begin(), methods.end(), pred); + if (it != methods.end()) { + return &*it; + } + return nullptr; +} + +void PandaFileTranslator::TranslateClasses(const panda_file::File &pf, const CString &methodName) +{ + RegionFactory *factory = ecmaVm_->GetRegionFactory(); + Span classIndexes = pf.GetClasses(); + uint32_t numMethods = 0; + + for (const uint32_t index : classIndexes) { + panda_file::File::EntityId classId(index); + if (pf.IsExternal(classId)) { + continue; + } + panda_file::ClassDataAccessor cda(pf, classId); + numMethods += cda.GetMethodsNumber(); + } + + auto methodsData = factory->AllocateBuffer(sizeof(JSMethod) * numMethods); + Span methods {static_cast(methodsData), numMethods}; + size_t methodIdx = 0; + + panda_file::File::StringData sd = {static_cast(methodName.size()), + reinterpret_cast(methodName.c_str())}; + for (const uint32_t index : classIndexes) { + panda_file::File::EntityId classId(index); + if (pf.IsExternal(classId)) { + continue; + } + panda_file::ClassDataAccessor cda(pf, classId); + cda.EnumerateMethods([this, &sd, &methods, &methodIdx, &pf](panda_file::MethodDataAccessor &mda) { + auto codeId = mda.GetCodeId(); + ASSERT(codeId.has_value()); + + JSMethod *method = &methods[methodIdx++]; + panda_file::CodeDataAccessor codeDataAccessor(pf, codeId.value()); + uint32_t codeSize = codeDataAccessor.GetCodeSize(); + + if (mainMethodIndex_ == 0 && pf.GetStringData(mda.GetNameId()) == sd) { + mainMethodIndex_ = mda.GetMethodId().GetOffset(); + } + + panda_file::ProtoDataAccessor pda(pf, mda.GetProtoId()); + InitializeMemory(method, nullptr, &pf, mda.GetMethodId(), codeDataAccessor.GetCodeId(), + mda.GetAccessFlags(), codeDataAccessor.GetNumArgs(), nullptr); + method->SetHotnessCounter(EcmaInterpreter::METHOD_HOTNESS_THRESHOLD); + const uint8_t *insns = codeDataAccessor.GetInstructions(); + if (this->translated_code_.find(insns) == this->translated_code_.end()) { + this->translated_code_.insert(insns); + this->TranslateBytecode(codeSize, insns, pf, method); + } + }); + } + + SetMethods(methods, numMethods); +} + +Program *PandaFileTranslator::GenerateProgram(const panda_file::File &pf) +{ + EcmaHandleScope handleScope(thread_); + + JSHandle program = factory_->NewProgram(); + JSHandle location = factory_->NewFromStdString(pf.GetFilename()); + + // +1 for program + JSHandle constpool = factory_->NewConstantPool(constpoolIndex_ + 1); + program->SetConstantPool(thread_, constpool.GetTaggedValue()); + program->SetLocation(thread_, location.GetTaggedValue()); + + JSHandle env = ecmaVm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetFunctionClassWithProto()); + JSHandle normalDynclass = JSHandle::Cast(env->GetFunctionClassWithoutProto()); + JSHandle asyncDynclass = JSHandle::Cast(env->GetAsyncFunctionClass()); + JSHandle generatorDynclass = JSHandle::Cast(env->GetGeneratorFunctionClass()); + JSHandle classDynclass = JSHandle::Cast(env->GetFunctionClassWithoutName()); + + for (const auto &it : constpoolMap_) { + ConstPoolValue value(it.second); + if (value.GetConstpoolType() == ConstPoolType::STRING) { + panda_file::File::EntityId id(it.first); + auto foundStr = pf.GetStringData(id); + auto string = factory_->GetRawStringFromStringTable(foundStr.data, foundStr.utf16_length); + constpool->Set(thread_, value.GetConstpoolIndex(), JSTaggedValue(string)); + } else if (value.GetConstpoolType() == ConstPoolType::BASE_FUNCTION) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + + JSHandle jsFunc = + factory_->NewJSFunctionByDynClass(method, dynclass, FunctionKind::BASE_CONSTRUCTOR); + constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); + jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::NC_FUNCTION) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + + JSHandle jsFunc = + factory_->NewJSFunctionByDynClass(method, normalDynclass, FunctionKind::NORMAL_FUNCTION); + constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); + jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::GENERATOR_FUNCTION) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + + JSHandle jsFunc = + factory_->NewJSFunctionByDynClass(method, generatorDynclass, FunctionKind::GENERATOR_FUNCTION); + // 26.3.4.3 prototype + // Whenever a GeneratorFunction instance is created another ordinary object is also created and + // is the initial value of the generator function's "prototype" property. + JSHandle objFun = env->GetObjectFunction(); + JSHandle initialGeneratorFuncPrototype = + factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSObject::SetPrototype(thread_, initialGeneratorFuncPrototype, env->GetGeneratorPrototype()); + jsFunc->SetProtoOrDynClass(thread_, initialGeneratorFuncPrototype); + + constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); + jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::ASYNC_FUNCTION) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + + JSHandle jsFunc = + factory_->NewJSFunctionByDynClass(method, asyncDynclass, FunctionKind::ASYNC_FUNCTION); + constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); + jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::CLASS_FUNCTION) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + JSHandle jsFunc = + factory_->NewJSFunctionByDynClass(method, classDynclass, FunctionKind::CLASS_CONSTRUCTOR); + constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); + jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::METHOD) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + + JSHandle jsFunc = + factory_->NewJSFunctionByDynClass(method, normalDynclass, FunctionKind::NORMAL_FUNCTION); + constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); + jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::OBJECT_LITERAL) { + size_t index = it.first; + JSMutableHandle elements(thread_, JSTaggedValue::Undefined()); + JSMutableHandle properties(thread_, JSTaggedValue::Undefined()); + LiteralDataExtractor::ExtractObjectDatas(thread_, &pf, index, elements, properties, this); + JSHandle obj = JSObject::CreateObjectFromProperties(thread_, properties); + + JSMutableHandle key(thread_, JSTaggedValue::Undefined()); + JSMutableHandle valueHandle(thread_, JSTaggedValue::Undefined()); + size_t elementsLen = elements->GetLength(); + for (size_t i = 0; i < elementsLen; i += 2) { // 2: Each literal buffer contains a pair of key-value. + key.Update(elements->Get(i)); + if (key->IsHole()) { + break; + } + valueHandle.Update(elements->Get(i + 1)); + JSObject::DefinePropertyByLiteral(thread_, obj, key, valueHandle); + } + constpool->Set(thread_, value.GetConstpoolIndex(), obj.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::ARRAY_LITERAL) { + size_t index = it.first; + JSHandle literal = + LiteralDataExtractor::GetDatasIgnoreType(thread_, &pf, static_cast(index)); + array_size_t length = literal->GetLength(); + + JSHandle arr(JSArray::ArrayCreate(thread_, JSTaggedNumber(length))); + JSMutableHandle key(thread_, JSTaggedValue::Undefined()); + JSMutableHandle arrValue(thread_, JSTaggedValue::Undefined()); + for (array_size_t i = 0; i < length; i++) { + key.Update(JSTaggedValue(static_cast(i))); + arrValue.Update(literal->Get(i)); + JSObject::DefinePropertyByLiteral(thread_, JSHandle::Cast(arr), key, arrValue); + } + constpool->Set(thread_, value.GetConstpoolIndex(), arr.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::CLASS_LITERAL) { + size_t index = it.first; + JSHandle literal = + LiteralDataExtractor::GetDatasIgnoreType(thread_, &pf, static_cast(index), this); + constpool->Set(thread_, value.GetConstpoolIndex(), literal.GetTaggedValue()); + } + } + { + auto method = const_cast(FindMethods(mainMethodIndex_)); + ASSERT(method != nullptr); + JSHandle mainFunc = + factory_->NewJSFunctionByDynClass(method, dynclass, FunctionKind::BASE_CONSTRUCTOR); + mainFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + program->SetMainFunction(thread_, mainFunc.GetTaggedValue()); + program->SetMethodsData(methods_); + program->SetNumberMethods(numMethods_); + // link program + constpool->Set(thread_, constpoolIndex_, program.GetTaggedValue()); + } + + return *program; +} + +void PandaFileTranslator::FixOpcode(uint8_t *pc) const +{ + auto opcode = static_cast(*pc); + + constexpr size_t numBuiltinsFormat = 20; + std::array skipTable = { + // NOLINTNEXTLINE(readability-magic-numbers) + 0U, 32U, 83U, 101U, 105U, 107U, 113U, 122U, 122U, 124U, + // NOLINTNEXTLINE(readability-magic-numbers) + 129U, 133U, 139U, 139U, 140U, 141U, 147U, 148U, 150U, 152U + }; + + switch (opcode) { + case BytecodeInstruction::Opcode::MOV_V4_V4: + *pc = static_cast(EcmaOpcode::MOV_V4_V4); + break; + case BytecodeInstruction::Opcode::MOV_DYN_V8_V8: + *pc = static_cast(EcmaOpcode::MOV_DYN_V8_V8); + break; + case BytecodeInstruction::Opcode::MOV_DYN_V16_V16: + *pc = static_cast(EcmaOpcode::MOV_DYN_V16_V16); + break; + case BytecodeInstruction::Opcode::LDA_STR_ID32: + *pc = static_cast(EcmaOpcode::LDA_STR_ID32); + break; + case BytecodeInstruction::Opcode::JMP_IMM8: + *pc = static_cast(EcmaOpcode::JMP_IMM8); + break; + case BytecodeInstruction::Opcode::JMP_IMM16: + *pc = static_cast(EcmaOpcode::JMP_IMM16); + break; + case BytecodeInstruction::Opcode::JMP_IMM32: + *pc = static_cast(EcmaOpcode::JMP_IMM32); + break; + case BytecodeInstruction::Opcode::JEQZ_IMM8: + *pc = static_cast(EcmaOpcode::JEQZ_IMM8); + break; + case BytecodeInstruction::Opcode::JEQZ_IMM16: + *pc = static_cast(EcmaOpcode::JEQZ_IMM16); + break; + case BytecodeInstruction::Opcode::JNEZ_IMM8: + *pc = static_cast(EcmaOpcode::JNEZ_IMM8); + break; + case BytecodeInstruction::Opcode::JNEZ_IMM16: + *pc = static_cast(EcmaOpcode::JNEZ_IMM16); + break; + case BytecodeInstruction::Opcode::LDA_DYN_V8: + *pc = static_cast(EcmaOpcode::LDA_DYN_V8); + break; + case BytecodeInstruction::Opcode::STA_DYN_V8: + *pc = static_cast(EcmaOpcode::STA_DYN_V8); + break; + case BytecodeInstruction::Opcode::LDAI_DYN_IMM32: + *pc = static_cast(EcmaOpcode::LDAI_DYN_IMM32); + break; + case BytecodeInstruction::Opcode::FLDAI_DYN_IMM64: + *pc = static_cast(EcmaOpcode::FLDAI_DYN_IMM64); + break; + case BytecodeInstruction::Opcode::RETURN_DYN: + *pc = static_cast(EcmaOpcode::RETURN_DYN); + break; + case BytecodeInstruction::Opcode::BUILTIN_ACC_IMM8: + case BytecodeInstruction::Opcode::BUILTIN_BIN2_IMM8_V8: + case BytecodeInstruction::Opcode::BUILTIN_TERN3_IMM8_V8_V8: + case BytecodeInstruction::Opcode::BUILTIN_QUATERN4_IMM8_V8_V8_V8: + case BytecodeInstruction::Opcode::BUILTIN_QUIN5_IMM8_V8_V8_V8_V8: + case BytecodeInstruction::Opcode::BUILTIN_R2I_IMM8_IMM16_V8: + case BytecodeInstruction::Opcode::BUILTIN_R3I_IMM8_IMM16_V8_V8: + case BytecodeInstruction::Opcode::BUILTIN_R4I_IMM8_IMM16_V8_V8_V8: + case BytecodeInstruction::Opcode::BUILTIN_ID_IMM8_ID32: + case BytecodeInstruction::Opcode::BUILTIN_MIDR_IMM8_ID16_V8: + case BytecodeInstruction::Opcode::BUILTIN_IDI_IMM8_ID32_IMM16: + case BytecodeInstruction::Opcode::BUILTIN_IDR3I_IMM8_ID32_IMM16_V8: + case BytecodeInstruction::Opcode::BUILTIN_IDR4I_IMM8_ID32_IMM16_V8_V8: + case BytecodeInstruction::Opcode::BUILTIN_I2R3_IMM8_IMM16_IMM16_V8: + case BytecodeInstruction::Opcode::BUILTIN_I2R2_IMM8_IMM16_IMM16: + case BytecodeInstruction::Opcode::BUILTIN_IMM_IMM8_IMM16: + case BytecodeInstruction::Opcode::BUILTIN_IMR2_IMM8_ID16_IMM16_V8_V8: { + const int index = + static_cast(opcode) - static_cast(BytecodeInstruction::Opcode::BUILTIN_ACC_IMM8); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto builtinId = *(pc + 1); + builtinId += skipTable[index]; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *pc = builtinId; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic, readability-magic-numbers) + *(pc + 1) = 0xFF; + break; + } + default: + LOG_ECMA(FATAL) << "Unsupported opcode: " << static_cast(opcode); + UNREACHABLE(); + } +} + +void PandaFileTranslator::FixInstructionId32(const BytecodeInstruction &inst, [[maybe_unused]] uint32_t index, + uint32_t fixOrder) const +{ + // NOLINTNEXTLINE(hicpp-use-auto) + auto pc = const_cast(inst.GetAddress()); + switch (inst.GetFormat()) { + case BytecodeInstruction::Format::ID32: { + uint8_t size = sizeof(uint32_t); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(pc + FixInstructionIndex::FIX_ONE, size, &index, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + break; + } + case BytecodeInstruction::Format::IMM8_ID16_V8: { + uint16_t u16Index = index; + uint8_t size = sizeof(uint16_t); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(pc + FixInstructionIndex::FIX_TWO, size, &u16Index, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + break; + } + case BytecodeInstruction::Format::IMM8_ID32: + case BytecodeInstruction::Format::IMM8_ID32_IMM16: + case BytecodeInstruction::Format::IMM8_ID32_IMM16_V8: + case BytecodeInstruction::Format::IMM8_ID32_IMM16_V8_V8: { + uint8_t size = sizeof(uint32_t); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(pc + FixInstructionIndex::FIX_TWO, size, &index, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + break; + } + case BytecodeInstruction::Format::IMM8_IMM16: { + ASSERT(static_cast(index) == index); + uint16_t u16Index = index; + uint8_t size = sizeof(uint16_t); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(pc + FixInstructionIndex::FIX_TWO, size, &u16Index, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + break; + } + case BytecodeInstruction::Format::IMM8_ID16_IMM16_V8_V8: { + // Usually, we fix one part of instruction one time. But as for instruction DefineClassWithBuffer, + // which use both method id and literal buffer id.Using fixOrder indicates fix Location. + if (fixOrder == 0) { + uint8_t size = sizeof(uint16_t); + ASSERT(static_cast(index) == index); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(pc + FixInstructionIndex::FIX_TWO, size, &index, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + break; + } + if (fixOrder == 1) { + ASSERT(static_cast(index) == index); + uint16_t u16Index = index; + uint8_t size = sizeof(uint16_t); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(pc + FixInstructionIndex::FIX_FOUR, size, &u16Index, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + break; + } + break; + } + default: + UNREACHABLE(); + } +} + +void PandaFileTranslator::TranslateBytecode(uint32_t insSz, const uint8_t *insArr, const panda_file::File &pf, + const JSMethod *method) +{ + auto bcIns = BytecodeInstruction(insArr); + auto bcInsLast = bcIns.JumpTo(insSz); + + while (bcIns.GetAddress() != bcInsLast.GetAddress()) { + { + if (bcIns.HasFlag(BytecodeInstruction::Flags::STRING_ID) && + BytecodeInstruction::HasId(bcIns.GetFormat(), 0)) { + auto index = GetOrInsertConstantPool(ConstPoolType::STRING, bcIns.GetId().AsFileId().GetOffset()); + FixInstructionId32(bcIns, index); + } else if (static_cast(bcIns.GetOpcode()) == + BytecodeInstruction::Opcode::BUILTIN_MIDR_IMM8_ID16_V8) { + auto methodId = pf.ResolveMethodIndex(method->GetFileId(), bcIns.GetId().AsIndex()).GetOffset(); + switch (bcIns.template GetImm()) { + uint32_t index; + case SecondInstructionOfMidr::DEFINE_FUNC_DYN: + index = GetOrInsertConstantPool(ConstPoolType::BASE_FUNCTION, methodId); + FixInstructionId32(bcIns, index); + break; + case SecondInstructionOfMidr::DEFINE_NC_FUNC_DYN: + index = GetOrInsertConstantPool(ConstPoolType::NC_FUNCTION, methodId); + FixInstructionId32(bcIns, index); + break; + case SecondInstructionOfMidr::DEFINE_GENERATOR_FUNC_DYN: + index = GetOrInsertConstantPool(ConstPoolType::GENERATOR_FUNCTION, methodId); + FixInstructionId32(bcIns, index); + break; + case SecondInstructionOfMidr::DEFINE_ASYNC_FUNC_DYN: + index = GetOrInsertConstantPool(ConstPoolType::ASYNC_FUNCTION, methodId); + FixInstructionId32(bcIns, index); + break; + case SecondInstructionOfMidr::DEFINE_METHOD: + index = GetOrInsertConstantPool(ConstPoolType::METHOD, methodId); + FixInstructionId32(bcIns, index); + break; + default: + UNREACHABLE(); + } + } else if (static_cast(bcIns.GetOpcode()) == + BytecodeInstruction::Opcode::BUILTIN_IMM_IMM8_IMM16) { + switch (bcIns.template GetImm()) { + uint32_t index; + case 0: + case 1: + break; + case SecondInstructionOfImm::CREATE_OBJECT_WITH_BUFFER: + // createobjectwithbuffer + index = GetOrInsertConstantPool(ConstPoolType::OBJECT_LITERAL, + bcIns.GetImm()); + FixInstructionId32(bcIns, index); + break; + case SecondInstructionOfImm::CREATE_ARRAY_WITH_BUFFER: + // createarraywithbuffer + index = GetOrInsertConstantPool(ConstPoolType::ARRAY_LITERAL, + bcIns.GetImm()); + FixInstructionId32(bcIns, index); + break; + case SecondInstructionOfImm::CREATE_OBJECT_HAVING_METHOD: + // createobjecthavingmethod + index = GetOrInsertConstantPool(ConstPoolType::OBJECT_LITERAL, + bcIns.GetImm()); + FixInstructionId32(bcIns, index); + break; + default: + break; + } + } else if (static_cast(bcIns.GetOpcode()) == + BytecodeInstruction::Opcode::BUILTIN_IMR2_IMM8_ID16_IMM16_V8_V8) { + if (bcIns.template GetImm() == 0) { + // defineclasswithbuffer + auto methodId = pf.ResolveMethodIndex(method->GetFileId(), bcIns.GetId().AsIndex()).GetOffset(); + uint32_t index1 = GetOrInsertConstantPool(ConstPoolType::CLASS_FUNCTION, methodId); + FixInstructionId32(bcIns, index1); + + uint32_t index2 = + GetOrInsertConstantPool(ConstPoolType::CLASS_LITERAL, + bcIns.GetImm()); + FixInstructionId32(bcIns, index2, 1); + } + } + } + // NOLINTNEXTLINE(hicpp-use-auto) + auto pc = const_cast(bcIns.GetAddress()); + bcIns = bcIns.GetNext(); + FixOpcode(pc); + } +} + +uint32_t PandaFileTranslator::GetOrInsertConstantPool(ConstPoolType type, uint32_t offset) +{ + auto it = constpoolMap_.find(offset); + if (it != constpoolMap_.cend()) { + ConstPoolValue value(it->second); + return value.GetConstpoolIndex(); + } + ASSERT(constpoolIndex_ != UINT32_MAX); + uint32_t index = constpoolIndex_++; + ConstPoolValue value(type, index); + constpoolMap_.insert({offset, value.GetValue()}); + return index; +} + +JSHandle PandaFileTranslator::DefineMethodById(uint32_t methodId, FunctionKind kind) const +{ + auto method = const_cast(FindMethods(methodId)); + ASSERT(method != nullptr); + + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle functionClass; + if (kind == FunctionKind::NORMAL_FUNCTION) { + functionClass = JSHandle::Cast(env->GetFunctionClassWithoutProto()); + } else { + functionClass = JSHandle::Cast(env->GetGeneratorFunctionClass()); + } + JSHandle jsFunc = factory_->NewJSFunctionByDynClass(method, functionClass, kind); + + if (kind == FunctionKind::GENERATOR_FUNCTION) { + JSHandle objFun = env->GetObjectFunction(); + JSHandle initialGeneratorFuncPrototype = + factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSObject::SetPrototype(thread_, initialGeneratorFuncPrototype, env->GetGeneratorPrototype()); + jsFunc->SetProtoOrDynClass(thread_, initialGeneratorFuncPrototype); + } + return jsFunc; +} +} // namespace panda::ecmascript diff --git a/ecmascript/class_linker/panda_file_translator.h b/ecmascript/class_linker/panda_file_translator.h new file mode 100644 index 0000000000000000000000000000000000000000..5547543a0bbe795fec4292fcda84b7c0ecd57c5f --- /dev/null +++ b/ecmascript/class_linker/panda_file_translator.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_PANDA_FILE_TRANSLATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_PANDA_FILE_TRANSLATOR_H + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_function.h" +#include "libpandafile/bytecode_instruction.h" +#include "libpandafile/code_data_accessor-inl.h" +#include "libpandafile/file-inl.h" +#include "utils/bit_field.h" + +namespace panda::ecmascript { +class JSThread; +class Program; + +class PandaFileTranslator { +public: + enum FixInstructionIndex : uint8_t { FIX_ONE = 1, FIX_TWO = 2, FIX_FOUR = 4 }; + + enum SecondInstructionOfMidr : uint8_t { + DEFINE_FUNC_DYN = 0, + DEFINE_NC_FUNC_DYN, + DEFINE_GENERATOR_FUNC_DYN, + DEFINE_ASYNC_FUNC_DYN, + DEFINE_METHOD + }; + + enum SecondInstructionOfImm : uint8_t { + CREATE_OBJECT_WITH_BUFFER = 2, + CREATE_ARRAY_WITH_BUFFER, + CREATE_OBJECT_HAVING_METHOD + }; + + explicit PandaFileTranslator(EcmaVM *vm); + ~PandaFileTranslator() = default; + NO_COPY_SEMANTIC(PandaFileTranslator); + NO_MOVE_SEMANTIC(PandaFileTranslator); + static JSHandle TranslatePandaFile(EcmaVM *vm, const panda_file::File &pf, + const CString &methodName); + JSHandle DefineMethodById(uint32_t methodId, FunctionKind kind) const; + +private: + enum class ConstPoolType : uint8_t { + STRING, + BASE_FUNCTION, + NC_FUNCTION, + GENERATOR_FUNCTION, + ASYNC_FUNCTION, + CLASS_FUNCTION, + METHOD, + ARRAY_LITERAL, + OBJECT_LITERAL, + CLASS_LITERAL, + }; + + class ConstPoolValue { + public: + ConstPoolValue(ConstPoolType type, uint32_t index) + : value_(ConstPoolIndexField::Encode(index) | ConstPoolTypeField::Encode(type)) + { + } + + explicit ConstPoolValue(uint64_t v) : value_(v) {} + ~ConstPoolValue() = default; + NO_COPY_SEMANTIC(ConstPoolValue); + NO_MOVE_SEMANTIC(ConstPoolValue); + + inline uint64_t GetValue() const + { + return value_; + } + + inline uint32_t GetConstpoolIndex() const + { + return ConstPoolIndexField::Get(value_); + } + + inline ConstPoolType GetConstpoolType() const + { + return ConstPoolTypeField::Get(value_); + } + + private: + // NOLINTNEXTLINE(readability-magic-numbers) + using ConstPoolIndexField = BitField; // 32: 32 bit + // NOLINTNEXTLINE(readability-magic-numbers) + using ConstPoolTypeField = BitField; // 32: offset, 4: 4bit + + uint64_t value_{0}; + }; + uint32_t GetOrInsertConstantPool(ConstPoolType type, uint32_t offset); + const JSMethod *FindMethods(uint32_t offset) const; + Program *GenerateProgram(const panda_file::File &pf); + void TranslateClasses(const panda_file::File &pf, const CString &methodName); + void TranslateBytecode(uint32_t insSz, const uint8_t *insArr, const panda_file::File &pf, const JSMethod *method); + void FixInstructionId32(const BytecodeInstruction &inst, uint32_t index, uint32_t fixOrder = 0) const; + void FixOpcode(uint8_t *pc) const; + + void SetMethods(Span methods, const uint32_t numMethods) + { + methods_ = methods.data(); + numMethods_ = numMethods; + } + + Span GetMethods() const + { + return {methods_, numMethods_}; + } + + EcmaVM *ecmaVm_; + ObjectFactory *factory_; + JSThread *thread_; + uint32_t constpoolIndex_{0}; + uint32_t numMethods_{0}; + uint32_t mainMethodIndex_{0}; + JSMethod *methods_ {nullptr}; + + std::unordered_map constpoolMap_; + std::set translated_code_; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_PANDA_FILE_TRANSLATOR_H diff --git a/ecmascript/class_linker/program_object-inl.h b/ecmascript/class_linker/program_object-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..ee1263311e764c8171bfa630aecef1dd2d29ea30 --- /dev/null +++ b/ecmascript/class_linker/program_object-inl.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_PROGRAM_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_PROGRAM_INL_H + +#include "program_object.h" +#include "ecmascript/mem/region_factory.h" + +namespace panda { +namespace ecmascript { +const uint8_t *LexicalFunction::GetInstructions() const +{ + JSTaggedValue inst = GetBytecode(); + void *buffer = JSNativePointer::Cast(inst.GetTaggedObject())->GetExternalPointer(); + return static_cast(buffer); +} + +JSTaggedValue ConstantPool::GetObjectFromCache(uint32_t index) const +{ + return Get(index); +} + +void Program::FreeMethodData(RegionFactory *factory) +{ + factory->FreeBuffer(GetMethodsData()); +} +} // namespace ecmascript +} // namespace panda +#endif // PANDA_RUNTIME_ECMASCRIPT_PROGRAM_INL_H \ No newline at end of file diff --git a/ecmascript/class_linker/program_object.h b/ecmascript/class_linker/program_object.h new file mode 100644 index 0000000000000000000000000000000000000000..b1fc7cfe3e4f07320a41bee95da75c01ae890be3 --- /dev/null +++ b/ecmascript/class_linker/program_object.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_PROGRAM_H +#define PANDA_RUNTIME_ECMASCRIPT_PROGRAM_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_tagged_value-inl.h" + +namespace panda { +namespace ecmascript { +class JSThread; +class RegionFactory; + +class Program : public ECMAObject { +public: + DECL_CAST(Program) + + static constexpr size_t LOCATION_OFFSET = sizeof(ECMAObject); + ACCESSORS(Location, LOCATION_OFFSET, CONSTANT_POOL_OFFSET) + ACCESSORS(ConstantPool, CONSTANT_POOL_OFFSET, MAIN_FUNCTION_OFFSET) + ACCESSORS(MainFunction, MAIN_FUNCTION_OFFSET, METHODS_DATA_OFFSET) + SET_GET_NATIVE_FIELD(MethodsData, JSMethod, METHODS_DATA_OFFSET, NUMBER_METHODS_OFFSET) + SET_GET_PRIMITIVE_FIELD(NumberMethods, uint32_t, NUMBER_METHODS_OFFSET, SIZE); + + inline void FreeMethodData(RegionFactory *factory); + + DECL_DUMP() + DECL_VISIT_OBJECT(LOCATION_OFFSET, METHODS_DATA_OFFSET) +}; + +class LexicalFunction : public ECMAObject { +public: + DECL_CAST(LexicalFunction) + + uint32_t GetNumVregs() const + { + return static_cast(GetNumberVRegs().GetInt()); + } + + uint32_t GetNumICSlots() const + { + return static_cast(GetNumberICSlots().GetInt()); + } + + inline const uint8_t *GetInstructions() const; + + static constexpr size_t NAME_OFFSET = sizeof(ECMAObject); + ACCESSORS(Name, NAME_OFFSET, NUMBER_VREGS_OFFSET) + ACCESSORS(NumberVRegs, NUMBER_VREGS_OFFSET, NUMBER_IC_SLOTS_OFFSET) + ACCESSORS(NumberICSlots, NUMBER_IC_SLOTS_OFFSET, BYTECODE_OFFSET) + ACCESSORS(Bytecode, BYTECODE_OFFSET, PROGRAM_OFFSET) + ACCESSORS(Program, PROGRAM_OFFSET, SIZE) + + DECL_VISIT_OBJECT(NAME_OFFSET, SIZE) +}; + +class ConstantPool : public TaggedArray { +public: + static ConstantPool *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsTaggedArray()); + return static_cast(object); + } + + inline JSTaggedValue GetObjectFromCache(uint32_t index) const; + DECL_DUMP() +}; +} // namespace ecmascript +} // namespace panda +#endif // PANDA_RUNTIME_ECMASCRIPT_PROGRAM_H diff --git a/ecmascript/common.h b/ecmascript/common.h new file mode 100644 index 0000000000000000000000000000000000000000..23fd9380242541333129472b95198a00ea29f3da --- /dev/null +++ b/ecmascript/common.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMMON_H +#define PANDA_RUNTIME_ECMASCRIPT_COMMON_H + +#include "libpandabase/macros.h" + +namespace panda { +namespace ecmascript { +enum BarrierMode { SKIP_BARRIER, WRITE_BARRIER, READ_BARRIER }; + +constexpr size_t NUM_MANDATORY_JSFUNC_ARGS = 3; + +#define PUBLIC_API PANDA_PUBLIC_API + +#ifdef NDEBUG +#define DUMP_API_ATTR +#else +#define DUMP_API_ATTR __attribute__((visibility ("default"), used)) +#endif +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_COMMON_H diff --git a/ecmascript/compiler/BUILD.gn b/ecmascript/compiler/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..d627f9fea2fd9434962b2dc6d0446279f8f5b1e1 --- /dev/null +++ b/ecmascript/compiler/BUILD.gn @@ -0,0 +1,184 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//build/ohos.gni") + +action("build_llvm_libs") { + script = "build_llvm.sh" + sources = [ "//ark/js_runtime/ecmascript/compiler/build_llvm.sh" ] + outputs = [ "${root_out_dir}/llvm" ] +} + +config("include_llvm") { + include_dirs = [ + "//third_party/llvm-project/llvm/include/", + "//third_party/llvm-project/build/include", + "//third_party/llvm-project/llvm/build/include", + ] +} + +ohos_shared_library("libark_jsoptimizer") { + sources = [ + "circuit.cpp", + "circuit_builder.cpp", + "circuit_visualizer.cpp", + "fastpath_optimizer.cpp", + "gate.cpp", + "llvm_ir_builder.cpp", + "llvm_mcjit_compiler.cpp", + "scheduler.cpp", + "stub_interface.cpp", + "stub_optimizer.cpp", + "type.cpp", + "verifier.cpp", + ] + + configs = [ + ":include_llvm", + "//ark/js_runtime:ark_jsruntime_config", + "//ark/js_runtime:ark_jsruntime_public_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + lib_dirs = [ "//third_party/llvm-project/build/lib" ] + + libs = [ + "stdc++", + "z", + "tinfo", + "LTO", + "LLVMTarget", + "LLVMObject", + "LLVMMC", + "LLVMSupport", + "LLVMCore", + "LLVMExecutionEngine", + "LLVMInterpreter", + "LLVMMCJIT", + "LLVMExegesis", + "LLVMRuntimeDyld", + "LLVMInstCombine", + "LLVMAnalysis", + "LLVMScalarOpts", + "LLVMBinaryFormat", + "LLVMDebugInfoDWARF", + "LLVMRemarks", + "LLVMTextAPI", + "LLVMScalarOpts", + "LLVMTransformUtils", + "LLVMBitReader", + "LLVMSystemZInfo", + "LLVMAsmPrinter", + "LLVMProfileData", + "LLVMBitstreamReader", + "LLVMSelectionDAG", + "LLVMGlobalISel", + "LLVMLTO", + "LLVMCFGuard", + "LLVMVectorize", + "LLVMDemangle", + "LLVMipo", + "LLVMInstrumentation", + "LLVMDebugInfoCodeView", + "LLVMAggressiveInstCombine", + "LLVMAsmParser", + "LLVMMipsAsmParser", + "LLVMMCParser", + "LLVMMIRParser", + "LLVMMSP430AsmParser", + "LLVMX86Info", + "LLVMAArch64Info", + "LLVMLanaiInfo", + "LLVMAMDGPUInfo", + "LLVMARMInfo", + "LLVMHexagonInfo", + "LLVMNVPTXInfo", + "LLVMRISCVInfo", + "LLVMSparcInfo", + "LLVMMipsInfo", + "LLVMWebAssemblyInfo", + "LLVMBPFInfo", + "LLVMMSP430Info", + "LLVMPowerPCInfo", + "LLVMXCoreInfo", + "LLVMARMDesc", + "LLVMAArch64Desc", + "LLVMSparcDesc", + "LLVMAMDGPUDesc", + "LLVMXCoreDesc", + "LLVMWebAssemblyDesc", + "LLVMNVPTXDesc", + "LLVMMSP430Desc", + "LLVMPowerPCDesc", + "LLVMMipsDesc", + "LLVMRISCVDesc", + "LLVMLanaiDesc", + "LLVMSystemZDesc", + "LLVMHexagonDesc", + "LLVMX86Desc", + "LLVMBPFDesc", + "LLVMSparcDisassembler", + "LLVMRISCVDisassembler", + "LLVMMipsDisassembler", + "LLVMXCoreDisassembler", + "LLVMPowerPCDisassembler", + "LLVMX86Disassembler", + "LLVMMSP430Disassembler", + "LLVMARMDisassembler", + "LLVMLanaiDisassembler", + "LLVMAArch64Disassembler", + "LLVMBPFDisassembler", + "LLVMSystemZDisassembler", + "LLVMMCDisassembler", + "LLVMWebAssemblyDisassembler", + "LLVMHexagonDisassembler", + "LLVMAMDGPUDisassembler", + "LLVMXCoreCodeGen", + "LLVMPowerPCCodeGen", + "LLVMNVPTXCodeGen", + "LLVMAArch64CodeGen", + "LLVMMSP430CodeGen", + "LLVMARMCodeGen", + "LLVMMipsCodeGen", + "LLVMSparcCodeGen", + "LLVMBPFCodeGen", + "LLVMHexagonCodeGen", + "LLVMAMDGPUCodeGen", + "LLVMRISCVCodeGen", + "LLVMWebAssemblyCodeGen", + "LLVMSystemZCodeGen", + "LLVMCodeGen", + "LLVMX86CodeGen", + "LLVMLanaiCodeGen", + "LLVMX86AsmParser", + "LLVMAMDGPUUtils", + "LLVMRISCVUtils", + "LLVMTransformUtils", + "LLVMAArch64Utils", + "LLVMARMUtils", + "LLVMX86Utils", + ] + + deps = [ + ":build_llvm_libs", + "$ark_root/libpandabase:libarkbase", + "//ark/js_runtime:libark_jsruntime", + ] + + output_extension = "so" + if (!is_standard_system) { + relative_install_dir = "ark" + } + subsystem_name = "ark" +} diff --git a/ecmascript/compiler/circuit.cpp b/ecmascript/compiler/circuit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4995c188e8b746dd9c37fb2ab096f1d93fd99c80 --- /dev/null +++ b/ecmascript/compiler/circuit.cpp @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/compiler/circuit.h" + +namespace kungfu { +Circuit::Circuit() : space({}), circuitSize(0), gateCounter(0), time(1), dataSection({}) +{ + this->NewGate(OpCode(OpCode::CIRCUIT_ROOT), 0, {}, TypeCode::NOTYPE); // circuit root + auto circuitRoot = Circuit::GetCircuitRoot(OpCode(OpCode::CIRCUIT_ROOT)); + this->NewGate(OpCode(OpCode::STATE_ENTRY), 0, {circuitRoot}, TypeCode::NOTYPE); + this->NewGate(OpCode(OpCode::DEPEND_ENTRY), 0, {circuitRoot}, TypeCode::NOTYPE); + this->NewGate(OpCode(OpCode::FRAMESTATE_ENTRY), 0, {circuitRoot}, TypeCode::NOTYPE); + this->NewGate(OpCode(OpCode::RETURN_LIST), 0, {circuitRoot}, TypeCode::NOTYPE); + this->NewGate(OpCode(OpCode::THROW_LIST), 0, {circuitRoot}, TypeCode::NOTYPE); + this->NewGate(OpCode(OpCode::CONSTANT_LIST), 0, {circuitRoot}, TypeCode::NOTYPE); + this->NewGate(OpCode(OpCode::ALLOCA_LIST), 0, {circuitRoot}, TypeCode::NOTYPE); + this->NewGate(OpCode(OpCode::ARG_LIST), 0, {circuitRoot}, TypeCode::NOTYPE); +} + +uint8_t *Circuit::AllocateSpace(size_t gateSize) +{ + this->circuitSize += gateSize; + if (UNLIKELY(this->GetSpaceDataSize() == 0)) { + this->SetSpaceDataSize(INITIAL_SPACE); + } + while (UNLIKELY(this->GetSpaceDataSize() < this->circuitSize)) { + this->SetSpaceDataSize(this->GetSpaceDataSize() * SCALE_RATE); + } + if (UNLIKELY(this->GetSpaceDataSize() > MAX_SPACE)) { + return nullptr; // abort compilation + } + if (UNLIKELY(this->GetSpaceDataStartPtrConst() == nullptr)) { + return nullptr; // abort compilation + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return this->GetDataPtr(this->circuitSize - gateSize); +} + +Gate *Circuit::AllocateGateSpace(size_t numIns) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast(this->AllocateSpace(Gate::GetGateSize(numIns)) + Gate::GetOutListSize(numIns)); +} + +// NOLINTNEXTLINE(modernize-avoid-c-arrays) +AddrShift Circuit::NewGate( + OpCode opcode, BitField bitfield, size_t numIns, const AddrShift inList[], TypeCode type, MarkCode mark) +{ +#ifndef NDEBUG + if (numIns != opcode.GetOpCodeNumIns(bitfield)) { + std::cerr << "Invalid input list!" + << " op=" << opcode.Str() << " bitfield=" << bitfield + << " expected_num_in=" << opcode.GetOpCodeNumIns(bitfield) << " actual_num_in=" << numIns + << std::endl; + UNREACHABLE(); + } +#endif + std::vector inPtrList(numIns); + auto gateSpace = this->AllocateGateSpace(numIns); + for (size_t idx = 0; idx < numIns; idx++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + inPtrList[idx] = (inList[idx] == Circuit::NullGate()) ? nullptr : this->LoadGatePtr(inList[idx]); + } + auto newGate = new (gateSpace) Gate(this->gateCounter, opcode, bitfield, inPtrList.data(), type, mark); + this->gateCounter++; + return this->SaveGatePtr(newGate); +} + +AddrShift Circuit::NewGate( + OpCode opcode, BitField bitfield, const std::vector &inList, TypeCode type, MarkCode mark) +{ + return this->NewGate(opcode, bitfield, inList.size(), inList.data(), type, mark); +} + +void Circuit::PrintAllGates() const +{ + const auto &gateList = this->GetAllGates(); + for (auto &gate : gateList) { + this->LoadGatePtrConst(gate)->Print(); + } +} + +std::vector Circuit::GetAllGates() const +{ + std::vector gateList; + gateList.push_back(0); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for (size_t out = sizeof(Gate); out < this->circuitSize; + out += Gate::GetGateSize( + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + reinterpret_cast(this->LoadGatePtrConst(AddrShift(out)))->GetIndex() + 1)) { + gateList.push_back( + this->SaveGatePtr(reinterpret_cast(this->LoadGatePtrConst(AddrShift(out)))->GetGateConst())); + } + return gateList; +} + +AddrShift Circuit::SaveGatePtr(const Gate *gate) const +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return static_cast(reinterpret_cast(gate) - this->GetDataPtrConst(0)); +} + +Gate *Circuit::LoadGatePtr(AddrShift shift) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast(this->GetDataPtr(shift)); +} + +const Gate *Circuit::LoadGatePtrConst(AddrShift shift) const +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast(this->GetDataPtrConst(shift)); +} + +AddrShift Circuit::GetCircuitRoot(OpCode opcode) +{ + switch (opcode) { + case OpCode::CIRCUIT_ROOT: + return sizeof(In) * 0 + sizeof(Out) * 0 + sizeof(Gate) * 0; // 0 0 0: offset of circuit root + case OpCode::STATE_ENTRY: + return sizeof(In) * 0 + sizeof(Out) * 1 + sizeof(Gate) * 1; // 0 1 1: offset of state entry + case OpCode::DEPEND_ENTRY: + return sizeof(In) * 1 + sizeof(Out) * 2 + sizeof(Gate) * 2; // 1 2 2: offset of depend entry + case OpCode::FRAMESTATE_ENTRY: + return sizeof(In) * 2 + sizeof(Out) * 3 + sizeof(Gate) * 3; // 2 3 3: offset of framestate entry + case OpCode::RETURN_LIST: + return sizeof(In) * 3 + sizeof(Out) * 4 + sizeof(Gate) * 4; // 3 4 4: offset of return list + case OpCode::THROW_LIST: + return sizeof(In) * 4 + sizeof(Out) * 5 + sizeof(Gate) * 5; // 4 5 5: offset of throw list + case OpCode::CONSTANT_LIST: + return sizeof(In) * 5 + sizeof(Out) * 6 + sizeof(Gate) * 6; // 5 6 6: offset of constant list + case OpCode::ALLOCA_LIST: + return sizeof(In) * 6 + sizeof(Out) * 7 + sizeof(Gate) * 7; // 6 7 7: offset of alloca list + case OpCode::ARG_LIST: + return sizeof(In) * 7 + sizeof(Out) * 8 + sizeof(Gate) * 8; // 7 8 8: offset of arg list + default: + UNREACHABLE(); + } +} + +Circuit::~Circuit() {} + +void Circuit::AdvanceTime() const +{ + auto &time = const_cast(this->time); + time++; + if (time == 0) { + time = 1; + this->ResetAllGateTimeStamps(); + } +} + +void Circuit::ResetAllGateTimeStamps() const +{ + const auto &gateList = this->GetAllGates(); + for (auto &gate : gateList) { + const_cast(this->LoadGatePtrConst(gate))->SetMark(MarkCode::EMPTY, 0); + } +} + +TimeStamp Circuit::GetTime() const +{ + return this->time; +} + +MarkCode Circuit::GetMark(AddrShift gate) const +{ + return this->LoadGatePtrConst(gate)->GetMark(this->GetTime()); +} + +void Circuit::SetMark(AddrShift gate, MarkCode mark) const +{ + const_cast(this->LoadGatePtrConst(gate))->SetMark(mark, this->GetTime()); +} + +bool Circuit::Verify(AddrShift gate) const +{ + return this->LoadGatePtrConst(gate)->Verify(); +} + +AddrShift Circuit::NullGate() +{ + return -1; +} + +bool Circuit::IsLoopHead(AddrShift gate) const +{ + if (gate != NullGate()) { + const Gate *curGate = this->LoadGatePtrConst(gate); + return curGate->GetOpCode() == OpCode::LOOP_BEGIN; + } + return false; +} + +bool Circuit::IsSelector(AddrShift gate) const +{ + if (gate != NullGate()) { + const Gate *curGate = this->LoadGatePtrConst(gate); + return curGate->GetOpCode() >= OpCode::VALUE_SELECTOR_JS && + curGate->GetOpCode() <= OpCode::VALUE_SELECTOR_FLOAT64; + } + return false; +} + +std::vector Circuit::GetInVector(AddrShift gate) const +{ + std::vector result; + const Gate *curGate = this->LoadGatePtrConst(gate); + for (size_t idx = 0; idx < curGate->GetNumIns(); idx++) { + result.push_back(this->SaveGatePtr(curGate->GetInGateConst(idx))); + } + return result; +} + +AddrShift Circuit::GetIn(AddrShift gate, size_t idx) const +{ + const Gate *curGate = this->LoadGatePtrConst(gate); + return this->SaveGatePtr(curGate->GetInGateConst(idx)); +} + +bool Circuit::IsInGateNull(AddrShift gate, size_t idx) const +{ + const Gate *curGate = this->LoadGatePtrConst(gate); + return curGate->GetInConst(idx)->IsGateNull(); +} + +bool Circuit::IsFirstOutNull(AddrShift gate) const +{ + const Gate *curGate = this->LoadGatePtrConst(gate); + return curGate->IsFirstOutNull(); +} + +std::vector Circuit::GetOutVector(AddrShift gate) const +{ + std::vector result; + const Gate *curGate = this->LoadGatePtrConst(gate); + if (!curGate->IsFirstOutNull()) { + const Out *curOut = curGate->GetFirstOutConst(); + result.push_back(this->SaveGatePtr(curOut->GetGateConst())); + while (!curOut->IsNextOutNull()) { + curOut = curOut->GetNextOutConst(); + result.push_back(this->SaveGatePtr(curOut->GetGateConst())); + } + } + return result; +} + +void Circuit::NewIn(AddrShift gate, size_t idx, AddrShift in) +{ + this->LoadGatePtr(gate)->NewIn(idx, this->LoadGatePtr(in)); +} + +void Circuit::ModifyIn(AddrShift gate, size_t idx, AddrShift in) +{ + this->LoadGatePtr(gate)->ModifyIn(idx, this->LoadGatePtr(in)); +} + +void Circuit::DeleteIn(AddrShift gate, size_t idx) +{ + this->LoadGatePtr(gate)->DeleteIn(idx); +} + +void Circuit::DeleteGate(AddrShift gate) +{ + this->LoadGatePtr(gate)->DeleteGate(); +} + +void Circuit::SetOpCode(AddrShift gate, OpCode opcode) +{ + this->LoadGatePtr(gate)->SetOpCode(opcode); +} + +OpCode Circuit::GetOpCode(AddrShift gate) const +{ + return this->LoadGatePtrConst(gate)->GetOpCode(); +} + +GateId Circuit::GetId(AddrShift gate) const +{ + return this->LoadGatePtrConst(gate)->GetId(); +} + +BitField Circuit::GetBitField(AddrShift gate) const +{ + return this->LoadGatePtrConst(gate)->GetBitField(); +} + +void Circuit::Print(AddrShift gate) const +{ + this->LoadGatePtrConst(gate)->Print(); +} + +std::vector Circuit::GetDataSection() const +{ + return this->dataSection; +} + +void Circuit::SetDataSection(const std::vector &data) +{ + this->dataSection = data; +} + +size_t Circuit::GetCircuitDataSize() const +{ + return this->circuitSize; +} + +const void *Circuit::GetSpaceDataStartPtrConst() const +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return this->GetDataPtrConst(0); +} + +const void *Circuit::GetSpaceDataEndPtrConst() const +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return this->GetDataPtrConst(this->circuitSize); +} + +const uint8_t *Circuit::GetDataPtrConst(size_t offset) const +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return this->space.data() + offset; +} + +uint8_t *Circuit::GetDataPtr(size_t offset) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return this->space.data() + offset; +} + +size_t Circuit::GetSpaceDataSize() const +{ + return this->space.size(); +} + +void Circuit::SetSpaceDataSize(size_t sz) +{ + return this->space.resize(sz); +} +} // namespace kungfu diff --git a/ecmascript/compiler/circuit.h b/ecmascript/compiler/circuit.h new file mode 100644 index 0000000000000000000000000000000000000000..36056bbd7d4cf89abb3e8377ed0203a6a02b4c9a --- /dev/null +++ b/ecmascript/compiler/circuit.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_CIRCUIT_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_CIRCUIT_H + +#include +#include +#include +#include +#include + +#include "ecmascript/compiler/gate.h" +#include "libpandabase/macros.h" +#include "securec.h" + +namespace kungfu { +const size_t INITIAL_SPACE = 1U << 0U; // this should be tuned +const size_t MAX_SPACE = 1U << 24U; // this should be tuned +const size_t SCALE_RATE = 1U << 1U; // this should be tuned +class ControlFlowBuilder; +class RegAllocLinearScan; +class Circuit { // note: calling NewGate could make all saved Gate* invalid +public: + Circuit(); + ~Circuit(); + Circuit(Circuit const &circuit) = default; + Circuit &operator=(Circuit const &circuit) = default; + Circuit(Circuit &&circuit) = default; + Circuit &operator=(Circuit &&circuit) = default; + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + AddrShift NewGate(OpCode op, BitField bitfield, size_t numIns, const AddrShift inList[], TypeCode type, + MarkCode mark = MarkCode::EMPTY); + AddrShift NewGate(OpCode op, BitField bitfield, const std::vector &inList, TypeCode type, + MarkCode mark = MarkCode::EMPTY); + void PrintAllGates() const; + [[nodiscard]] std::vector GetAllGates() const; + [[nodiscard]] static AddrShift GetCircuitRoot(OpCode opcode); + void AdvanceTime() const; + void ResetAllGateTimeStamps() const; + [[nodiscard]] static AddrShift NullGate(); + [[nodiscard]] bool IsLoopHead(AddrShift gate) const; + [[nodiscard]] bool IsSelector(AddrShift gate) const; + [[nodiscard]] AddrShift GetIn(AddrShift gate, size_t idx) const; + [[nodiscard]] bool IsInGateNull(AddrShift gate, size_t idx) const; + [[nodiscard]] bool IsFirstOutNull(AddrShift gate) const; + [[nodiscard]] std::vector GetInVector(AddrShift gate) const; + [[nodiscard]] std::vector GetOutVector(AddrShift gate) const; + void NewIn(AddrShift gate, size_t idx, AddrShift in); + void ModifyIn(AddrShift gate, size_t idx, AddrShift in); + void DeleteIn(AddrShift gate, size_t idx); + void DeleteGate(AddrShift gate); + [[nodiscard]] GateId GetId(AddrShift gate) const; + [[nodiscard]] BitField GetBitField(AddrShift gate) const; + void Print(AddrShift gate) const; + void SetOpCode(AddrShift gate, OpCode opcode); + [[nodiscard]] OpCode GetOpCode(AddrShift gate) const; + [[nodiscard]] TimeStamp GetTime() const; + [[nodiscard]] MarkCode GetMark(AddrShift gate) const; + void SetMark(AddrShift gate, MarkCode mark) const; + [[nodiscard]] bool Verify(AddrShift gate) const; + [[nodiscard]] Gate *LoadGatePtr(AddrShift shift); + [[nodiscard]] const Gate *LoadGatePtrConst(AddrShift shift) const; + [[nodiscard]] AddrShift SaveGatePtr(const Gate *gate) const; + [[nodiscard]] std::vector GetDataSection() const; + void SetDataSection(const std::vector &data); + [[nodiscard]] size_t GetCircuitDataSize() const; + [[nodiscard]] const void *GetSpaceDataStartPtrConst() const; + [[nodiscard]] const void *GetSpaceDataEndPtrConst() const; + [[nodiscard]] const uint8_t *GetDataPtrConst(size_t offset) const; + [[nodiscard]] uint8_t *GetDataPtr(size_t offset); + [[nodiscard]] size_t GetSpaceDataSize() const; + void SetSpaceDataSize(size_t sz); + +private: + uint8_t *AllocateSpace(size_t gateSize); + Gate *AllocateGateSpace(size_t numIns); + +private: + std::vector space; + size_t circuitSize; + size_t gateCounter; + TimeStamp time; + std::vector dataSection; +}; +} // namespace kungfu + +#endif // PANDA_RUNTIME_ECMASCRIPT_COMPILER_CIRCUIT_H diff --git a/ecmascript/compiler/circuit_builder.cpp b/ecmascript/compiler/circuit_builder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..119fc6f7aacbc651120571098ccd235f2ae7f071 --- /dev/null +++ b/ecmascript/compiler/circuit_builder.cpp @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/compiler/circuit_builder.h" +#include "include/coretypes/tagged_value.h" +#include "utils/bit_utils.h" + +namespace kungfu { +AddrShift CircuitBuilder::NewArguments(size_t index) +{ + auto argListOfCircuit = Circuit::GetCircuitRoot(OpCode(OpCode::ARG_LIST)); + return circuit_->NewGate(OpCode(OpCode::JS_ARG), index, {argListOfCircuit}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewMerge(AddrShift *inList, size_t controlCount) +{ + return circuit_->NewGate(OpCode(OpCode::MERGE), controlCount, controlCount, inList, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewSelectorGate(OpCode opCode, AddrShift control, int valueCounts) +{ + std::vector inList; + inList.push_back(control); + for (int i = 0; i < valueCounts; i++) { + inList.push_back(Circuit::NullGate()); + } + + return circuit_->NewGate(opCode, valueCounts, inList, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewIntegerConstant(int32_t val) +{ + auto constantList = Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST)); + return circuit_->NewGate(OpCode(OpCode::INT32_CONSTANT), val, {constantList}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewInteger64Constant(int64_t val) +{ + auto constantList = Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST)); + return circuit_->NewGate(OpCode(OpCode::INT64_CONSTANT), val, {constantList}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewBooleanConstant(bool val) +{ + auto constantList = Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST)); + return circuit_->NewGate(OpCode(OpCode::INT32_CONSTANT), val ? 1 : 0, {constantList}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewDoubleConstant(double val) +{ + auto constantList = Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST)); + return circuit_->NewGate(OpCode(OpCode::FLOAT64_CONSTANT), bit_cast(val), + {constantList}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::UndefineConstant() +{ + auto constantList = Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST)); + return circuit_->NewGate(OpCode(OpCode::INT64_CONSTANT), panda::coretypes::TaggedValue::VALUE_UNDEFINED, + {constantList}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::HoleConstant() +{ + auto constantList = Circuit::GetCircuitRoot(OpCode(OpCode::CONSTANT_LIST)); + // NOTE: add bitfield value here + return circuit_->NewGate(OpCode(OpCode::JS_CONSTANT), panda::coretypes::TaggedValue::VALUE_HOLE, {constantList}, + TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::Branch(AddrShift state, AddrShift condition) +{ + return circuit_->NewGate(OpCode(OpCode::IF_BRANCH), 0, {state, condition}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::SwitchBranch(AddrShift state, AddrShift index, int caseCounts) +{ + return circuit_->NewGate(OpCode(OpCode::SWITCH_BRANCH), caseCounts, {state, index}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::Return(AddrShift state, AddrShift value) +{ + auto dependEntry = Circuit::GetCircuitRoot(OpCode(OpCode::DEPEND_ENTRY)); + auto returnList = Circuit::GetCircuitRoot(OpCode(OpCode::RETURN_LIST)); + return circuit_->NewGate(OpCode(OpCode::RETURN), 0, {state, dependEntry, value, returnList}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::Goto(AddrShift state) +{ + return circuit_->NewGate(OpCode(OpCode::ORDINARY_BLOCK), 0, {state}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::LoopBegin(AddrShift state) +{ + auto nullGate = Circuit::NullGate(); + return circuit_->NewGate(OpCode(OpCode::LOOP_BEGIN), 0, {state, nullGate}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::LoopEnd(AddrShift state) +{ + return circuit_->NewGate(OpCode(OpCode::LOOP_BACK), 0, {state}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewIfTrue(AddrShift ifBranch) +{ + return circuit_->NewGate(OpCode(OpCode::IF_TRUE), 0, {ifBranch}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewIfFalse(AddrShift ifBranch) +{ + return circuit_->NewGate(OpCode(OpCode::IF_FALSE), 0, {ifBranch}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewSwitchCase(AddrShift switchBranch, int32_t value) +{ + return circuit_->NewGate(OpCode(OpCode::SWITCH_CASE), value, {switchBranch}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewDefaultCase(AddrShift switchBranch) +{ + return circuit_->NewGate(OpCode(OpCode::DEFAULT_CASE), 0, {switchBranch}, TypeCode::NOTYPE); +} + +OpCode CircuitBuilder::GetStoreOpCodeFromMachineType(MachineType type) +{ + switch (type) { + case INT8_TYPE: + return OpCode(OpCode::INT8_STORE); + case INT16_TYPE: + return OpCode(OpCode::INT16_STORE); + case INT32_TYPE: + return OpCode(OpCode::INT32_STORE); + case INT64_TYPE: + return OpCode(OpCode::INT64_STORE); + case BOOL_TYPE: + return OpCode(OpCode::INT32_STORE); + case UINT8_TYPE: + return OpCode(OpCode::INT8_STORE); + case UINT16_TYPE: + return OpCode(OpCode::INT16_STORE); + case UINT32_TYPE: + return OpCode(OpCode::INT32_STORE); + case UINT64_TYPE: + case POINTER_TYPE: + case TAGGED_TYPE: + case TAGGED_POINTER_TYPE: + return OpCode(OpCode::INT64_STORE); + case FLOAT32_TYPE: + return OpCode(OpCode::FLOAT32_STORE); + case FLOAT64_TYPE: + return OpCode(OpCode::FLOAT64_STORE); + default: + UNREACHABLE(); + } +} + +OpCode CircuitBuilder::GetLoadOpCodeFromMachineType(MachineType type) +{ + switch (type) { + case INT8_TYPE: + return OpCode(OpCode::INT8_LOAD); + case INT16_TYPE: + return OpCode(OpCode::INT16_LOAD); + case INT32_TYPE: + return OpCode(OpCode::INT32_LOAD); + case INT64_TYPE: + return OpCode(OpCode::INT64_LOAD); + case BOOL_TYPE: + return OpCode(OpCode::INT32_LOAD); + case UINT8_TYPE: + return OpCode(OpCode::INT8_LOAD); + case UINT16_TYPE: + return OpCode(OpCode::INT16_LOAD); + case UINT32_TYPE: + return OpCode(OpCode::INT32_LOAD); + case UINT64_TYPE: + case POINTER_TYPE: + case TAGGED_TYPE: + case TAGGED_POINTER_TYPE: + return OpCode(OpCode::INT64_LOAD); + case FLOAT32_TYPE: + return OpCode(OpCode::FLOAT32_LOAD); + case FLOAT64_TYPE: + return OpCode(OpCode::FLOAT64_LOAD); + default: + UNREACHABLE(); + } +} + +OpCode CircuitBuilder::GetSelectOpCodeFromMachineType(MachineType type) +{ + switch (type) { + case INT8_TYPE: + return OpCode(OpCode::VALUE_SELECTOR_INT8); + case INT16_TYPE: + return OpCode(OpCode::VALUE_SELECTOR_INT16); + case INT32_TYPE: + return OpCode(OpCode::VALUE_SELECTOR_INT32); + case INT64_TYPE: + return OpCode(OpCode::VALUE_SELECTOR_INT64); + case BOOL_TYPE: + return OpCode(OpCode::VALUE_SELECTOR_INT1); + case UINT8_TYPE: + return OpCode(OpCode::VALUE_SELECTOR_INT8); + case UINT16_TYPE: + return OpCode(OpCode::VALUE_SELECTOR_INT16); + case UINT32_TYPE: + return OpCode(OpCode::VALUE_SELECTOR_INT32); + case UINT64_TYPE: + case POINTER_TYPE: + case TAGGED_TYPE: + case TAGGED_POINTER_TYPE: + return OpCode(OpCode::VALUE_SELECTOR_INT64); + case FLOAT32_TYPE: + return OpCode(OpCode::VALUE_SELECTOR_FLOAT32); + case FLOAT64_TYPE: + return OpCode(OpCode::VALUE_SELECTOR_FLOAT64); + default: + UNREACHABLE(); + } +} + +AddrShift CircuitBuilder::NewLoadGate(MachineType type, AddrShift val) +{ + auto dependEntry = Circuit::GetCircuitRoot(OpCode(OpCode::DEPEND_ENTRY)); + OpCode op = GetLoadOpCodeFromMachineType(type); + return circuit_->NewGate(op, type, {dependEntry, val}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewStoreGate(MachineType type, AddrShift ptr, AddrShift val) +{ + auto dependEntry = Circuit::GetCircuitRoot(OpCode(OpCode::DEPEND_ENTRY)); + OpCode op = GetStoreOpCodeFromMachineType(type); + return circuit_->NewGate(op, type, {dependEntry, val, ptr}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewArithMeticGate(OpCode opcode, AddrShift left, AddrShift right) +{ + return circuit_->NewGate(opcode, 0, {left, right}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewArithMeticGate(OpCode opcode, AddrShift value) +{ + return circuit_->NewGate(opcode, 0, {value}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewLogicGate(OpCode opcode, AddrShift left, AddrShift right) +{ + return circuit_->NewGate(opcode, MachineType::BOOL_TYPE, {left, right}, TypeCode::NOTYPE); +} + +AddrShift CircuitBuilder::NewLogicGate(OpCode opcode, AddrShift value) +{ + return circuit_->NewGate(opcode, MachineType::BOOL_TYPE, {value}, TypeCode::NOTYPE); +} + +OpCode CircuitBuilder::GetCallOpCodeFromMachineType(MachineType type) +{ + switch (type) { + case NONE_TYPE: + return OpCode(OpCode::CALL); + case INT8_TYPE: + return OpCode(OpCode::INT8_CALL); + case INT16_TYPE: + return OpCode(OpCode::INT16_CALL); + case INT32_TYPE: + return OpCode(OpCode::INT32_CALL); + case INT64_TYPE: + return OpCode(OpCode::INT64_CALL); + case BOOL_TYPE: + return OpCode(OpCode::INT1_CALL); + case UINT8_TYPE: + return OpCode(OpCode::INT8_CALL); + case UINT16_TYPE: + return OpCode(OpCode::INT16_CALL); + case UINT32_TYPE: + return OpCode(OpCode::INT32_CALL); + case UINT64_TYPE: + case POINTER_TYPE: + case TAGGED_TYPE: + case TAGGED_POINTER_TYPE: + return OpCode(OpCode::INT64_CALL); + case FLOAT32_TYPE: + return OpCode(OpCode::FLOAT32_CALL); + case FLOAT64_TYPE: + return OpCode(OpCode::FLOAT64_CALL); + default: + UNREACHABLE(); + } +} + +AddrShift CircuitBuilder::NewCallGate(StubInterfaceDescriptor *descriptor, AddrShift target, + std::initializer_list args) +{ + std::vector inputs; + auto dependEntry = Circuit::GetCircuitRoot(OpCode(OpCode::DEPEND_ENTRY)); + inputs.push_back(dependEntry); + inputs.push_back(target); + for (auto arg : args) { + inputs.push_back(arg); + } + OpCode opcode = GetCallOpCodeFromMachineType(descriptor->GetReturnType()); + + return circuit_->NewGate(opcode, args.size() + 1, inputs, TypeCode::JS_ANY); +} + +AddrShift CircuitBuilder::NewCallGate(StubInterfaceDescriptor *descriptor, AddrShift target, AddrShift depend, + std::initializer_list args) +{ + std::vector inputs; + inputs.push_back(depend); + inputs.push_back(target); + for (auto arg : args) { + inputs.push_back(arg); + } + OpCode opcode = GetCallOpCodeFromMachineType(descriptor->GetReturnType()); + return circuit_->NewGate(opcode, args.size() + 1, inputs, TypeCode::JS_ANY); +} +AddrShift CircuitBuilder::Alloca(int size) +{ + auto allocaList = Circuit::GetCircuitRoot(OpCode(OpCode::ALLOCA_LIST)); + return circuit_->NewGate(OpCode(OpCode::ALLOCA), size, {allocaList}, TypeCode::NOTYPE); +} +} // namespace kungfu diff --git a/ecmascript/compiler/circuit_builder.h b/ecmascript/compiler/circuit_builder.h new file mode 100644 index 0000000000000000000000000000000000000000..3289590eb62c8d61d4a7cfdef01e27edc036cce6 --- /dev/null +++ b/ecmascript/compiler/circuit_builder.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_CIRCUIT_BUILDER_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_CIRCUIT_BUILDER_H + +#include "ecmascript/compiler/circuit.h" +#include "ecmascript/compiler/gate.h" +#include "ecmascript/compiler/machine_type.h" +#include "ecmascript/compiler/stub_interface.h" + +namespace kungfu { +class CircuitBuilder { +public: + explicit CircuitBuilder(Circuit *circuit) : circuit_(circuit) {} + ~CircuitBuilder() = default; + NO_MOVE_SEMANTIC(CircuitBuilder); + NO_COPY_SEMANTIC(CircuitBuilder); + AddrShift NewArguments(size_t index); + AddrShift NewMerge(AddrShift *in, size_t controlCount); + AddrShift NewSelectorGate(OpCode opcode, AddrShift control, int valueCounts); + AddrShift NewIntegerConstant(int32_t value); + AddrShift NewInteger64Constant(int64_t value); + AddrShift NewWord64Constant(uint64_t val); + AddrShift NewBooleanConstant(bool value); + AddrShift NewDoubleConstant(double value); + AddrShift UndefineConstant(); + AddrShift HoleConstant(); + AddrShift Alloca(int size); + AddrShift Branch(AddrShift state, AddrShift condition); + AddrShift SwitchBranch(AddrShift state, AddrShift index, int caseCounts); + AddrShift Return(AddrShift state, AddrShift value); + AddrShift Goto(AddrShift state); + AddrShift LoopBegin(AddrShift state); + AddrShift LoopEnd(AddrShift state); + AddrShift NewIfTrue(AddrShift ifBranch); + AddrShift NewIfFalse(AddrShift ifBranch); + AddrShift NewSwitchCase(AddrShift switchBranch, int32_t value); + AddrShift NewDefaultCase(AddrShift switchBranch); + AddrShift NewLoadGate(MachineType type, AddrShift val); + AddrShift NewStoreGate(MachineType type, AddrShift ptr, AddrShift val); + AddrShift NewArithMeticGate(OpCode opcode, AddrShift left, AddrShift right); + AddrShift NewArithMeticGate(OpCode opcode, AddrShift value); + AddrShift NewLogicGate(OpCode opcode, AddrShift left, AddrShift right); + AddrShift NewLogicGate(OpCode opcode, AddrShift value); + AddrShift NewCallGate(StubInterfaceDescriptor *descriptor, AddrShift target, + std::initializer_list args); + AddrShift NewCallGate(StubInterfaceDescriptor *descriptor, AddrShift target, AddrShift depend, + std::initializer_list args); + static OpCode GetLoadOpCodeFromMachineType(MachineType type); + static OpCode GetStoreOpCodeFromMachineType(MachineType type); + static OpCode GetSelectOpCodeFromMachineType(MachineType type); + static OpCode GetCallOpCodeFromMachineType(MachineType type); + +private: + Circuit *circuit_; +}; +} // namespace kungfu + +#endif // PANDA_RUNTIME_ECMASCRIPT_COMPILER_CIRCUIT_BUILDER_H \ No newline at end of file diff --git a/ecmascript/compiler/circuit_visualizer.cpp b/ecmascript/compiler/circuit_visualizer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d65858818a7a1a1be5d5ac7c8a8fe95711ad758 --- /dev/null +++ b/ecmascript/compiler/circuit_visualizer.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ecmascript/compiler/circuit_visualizer.h" +#include "ecmascript/compiler/circuit.h" +#include "ecmascript/compiler/gate.h" + +namespace kungfu { +#ifndef NDEBUG +void CircuitVisualizer::DumpDFG() {} + +void CircuitVisualizer::PrintGateVisit(const Gate *node, std::ostream &os, int depth, int indentation) +{ + for (int i = 0; i < indentation; ++i) { + os << "---"; + } + if (node != nullptr) { + PrintSingleGate(node, os); + } else { + os << "(NULL)"; + return; + } + os << std::endl; + if (depth <= 0) { + return; + } + + if (!node->IsFirstOutNull()) { + Out *curOut = node->GetFirstOut(); + PrintGateVisit(curOut->GetGate(), os, depth - 1, indentation + 1); + while (!curOut->IsNextOutNull()) { + curOut = curOut->GetNextOut(); + PrintGateVisit(curOut->GetGate(), os, depth - 1, indentation + 1); + } + } +} + +void CircuitVisualizer::PrintGate(const Gate *node, int depth) +{ + PrintGateVisit(node, std::cout, depth, 0); + std::flush(std::cout); +} + +void CircuitVisualizer::PrintSingleGate(const Gate *node, std::ostream &os) +{ + ASSERT(node != nullptr); + (void)os; + node->Print(); +} + +#endif +} // namespace kungfu diff --git a/ecmascript/compiler/circuit_visualizer.h b/ecmascript/compiler/circuit_visualizer.h new file mode 100644 index 0000000000000000000000000000000000000000..97cf12f27b32ee03fb60c2a9e8338d435af10cb5 --- /dev/null +++ b/ecmascript/compiler/circuit_visualizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_CIRCUIT_VISUALIZER_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_CIRCUIT_VISUALIZER_H + +#include +#include +#include + +namespace kungfu { +#ifndef NDEBUG +// This class dump graph in graphviz format +// Option: --compiler-graph-visualizer + +class CircuitVisualizer { +public: + explicit CircuitVisualizer(Circuit *graph) : graph_(graph) {} + + void CreateDumpFile(const char *file_name); + void DumpDFG(); + void PrintGate(Gate *node, int depth); + ~CircuitVisualizer() = default; +private: + void PrintGateVisit(Gate *node, std::ostream &os, int depth, int indentation = 0); + void PrintSingleGate(Gate *node, std::ostream &os); + Circuit *graph_; + std::ofstream dump_output_{nullptr}; + const char *pass_name_{nullptr}; + std::map node_info_; +}; +#endif +} // namespace kungfu + +#endif \ No newline at end of file diff --git a/ecmascript/compiler/fast_stub_define.h b/ecmascript/compiler/fast_stub_define.h new file mode 100644 index 0000000000000000000000000000000000000000..e6e909ef1d3ee88959f5e8eca20df117de62ed65 --- /dev/null +++ b/ecmascript/compiler/fast_stub_define.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_FASTSTUB_DEFINE_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_FASTSTUB_DEFINE_H + +namespace kungfu { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define EXTERNAL_RUNTIMESTUB_LIST(V) \ + V(AddElementInternal, 5) \ + V(CallSetter, 2) \ + V(ThrowTypeError, 2) \ + V(JSProxySetProperty, 6) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define EXTERNAL_REFRENCE_STUB_LIST(V) \ + V(GetHash32, 2) \ + V(PhiTest, 1) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FAST_RUNTIME_STUB_LIST(V) \ + V(FastAdd, 2) \ + V(FastSub, 2) \ + V(FastMul, 2) \ + V(FastDiv, 2) \ + V(FastMod, 2) \ + V(FastEqual, 2) \ + V(FastTypeOf, 2) \ + V(FastStrictEqual, 2) \ + V(IsSpecialIndexedObjForSet, 1) \ + V(IsSpecialIndexedObjForGet, 1) \ + V(GetPropertyByName, 3) \ + V(GetElement, 2) \ + V(SetElement, 5) \ + V(SetPropertyByName, 5) \ + V(SetGlobalOwnProperty, 5) \ + V(GetGlobalOwnProperty, 3) \ + V(SetOwnPropertyByName, 4) \ + V(SetOwnElement, 4) \ + V(FastSetProperty, 5) \ + V(FastGetProperty, 3) \ + V(FindOwnProperty, 6) \ + V(FindOwnElement, 2) \ + V(NewLexicalEnvDyn, 4) \ + V(FindOwnProperty2, 3) \ + V(FindOwnElement2, 5) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CALL_STUB_LIST(V) \ + FAST_RUNTIME_STUB_LIST(V) \ + EXTERNAL_REFRENCE_STUB_LIST(V) \ + EXTERNAL_RUNTIMESTUB_LIST(V) +} // namespace kungfu +#endif // PANDA_RUNTIME_ECMASCRIPT_COMPILER_FASTSTUB_DEFINE_H \ No newline at end of file diff --git a/ecmascript/compiler/fastpath_optimizer.cpp b/ecmascript/compiler/fastpath_optimizer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..65ffd6a44912add99992bd9855921a49f6d38e07 --- /dev/null +++ b/ecmascript/compiler/fastpath_optimizer.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/compiler/fastpath_optimizer.h" +#include "ecmascript/js_array.h" +#include "ecmascript/tagged_hash_table-inl.h" + +namespace kungfu { +void FastArrayLoadElementOptimizer::GenerateCircuit() +{ + auto env = GetEnvironment(); + AddrShift aVal = Int64Argument(0); + AddrShift indexVal = Int32Argument(1); + + // load a.length + AddrShift lengthOffset = GetInteger32Constant(panda::ecmascript::JSArray::GetArrayLengthOffset()); + if (PtrValueCode() == ValueCode::INT64) { + lengthOffset = SExtInt32ToInt64(lengthOffset); + } else if (PtrValueCode() == ValueCode::INT32) { + aVal = TruncInt64ToInt32(aVal); + } + AddrShift taggegLength = Load(MachineType::TAGGED_TYPE, aVal, lengthOffset); + + AddrShift intLength = TaggedCastToInt32(taggegLength); + // if index < length + StubOptimizerLabel ifTrue(env); + StubOptimizerLabel ifFalse(env); + Branch(Int32LessThan(indexVal, intLength), &ifTrue, &ifFalse); + Bind(&ifTrue); + Return(LoadFromObject(MachineType::TAGGED_TYPE, aVal, indexVal)); + Bind(&ifFalse); + Return(GetUndefinedConstant()); +} + +FastRuntimeStubs::FastRuntimeStubs() + : fastRuntimeEnvs_{ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FAST_RUNTIME_STUB_ENV(name, arguments) Environment(#name, arguments), + FAST_RUNTIME_STUB_LIST(FAST_RUNTIME_STUB_ENV) +#undef FAST_RUNTIME_STUB_ENV + }, fastRuntimeOptimizers_{new FastArrayLoadElementOptimizer(&fastRuntimeEnvs_[1])} +{ +} +} // namespace kungfu \ No newline at end of file diff --git a/ecmascript/compiler/fastpath_optimizer.h b/ecmascript/compiler/fastpath_optimizer.h new file mode 100644 index 0000000000000000000000000000000000000000..a59cff4d0b8b60e131a88ce082c01855955a1440 --- /dev/null +++ b/ecmascript/compiler/fastpath_optimizer.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_FASTPATH_OPTIMIZER_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_FASTPATH_OPTIMIZER_H + +#include "ecmascript/compiler/fast_stub_define.h" +#include "ecmascript/compiler/stub_optimizer.h" + +namespace kungfu { +class FastArrayLoadElementOptimizer : public StubOptimizer { +public: + explicit FastArrayLoadElementOptimizer(Environment *env) : StubOptimizer(env) {} + ~FastArrayLoadElementOptimizer() = default; + NO_MOVE_SEMANTIC(FastArrayLoadElementOptimizer); + NO_COPY_SEMANTIC(FastArrayLoadElementOptimizer); + void GenerateCircuit() override; + uint8_t *AllocMachineCode() + { + static constexpr int PROT = PROT_READ | PROT_WRITE | PROT_EXEC; // NOLINT(hicpp-signed-bitwise) + static constexpr int FLAGS = MAP_ANONYMOUS | MAP_SHARED; // NOLINT(hicpp-signed-bitwise) + auto machineCode = static_cast(mmap(nullptr, MAX_MACHINE_CODE_SIZE, PROT, FLAGS, -1, 0)); + return machineCode; + } + void FreeMachineCode(uint8_t *machineCode) + { + munmap(machineCode, MAX_MACHINE_CODE_SIZE); + } + +private: + const size_t MAX_MACHINE_CODE_SIZE = (1U << 20U); +}; + +class FastRuntimeStubs { +public: + static FastRuntimeStubs &GetInstance() + { + static FastRuntimeStubs instance; + return instance; + } + enum FastRuntimeStubId { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FAST_RUNTIME_STUB_ID(name, counter) ID_##name, + FAST_RUNTIME_STUB_LIST(FAST_RUNTIME_STUB_ID) +#undef FAST_RUNTIME_STUB_ID + FAST_STUB_MAXNUMBER, + }; + + void GenerateFastRuntimeStubs(); + +private: + FastRuntimeStubs(); + ~FastRuntimeStubs(); + NO_MOVE_SEMANTIC(FastRuntimeStubs); + NO_COPY_SEMANTIC(FastRuntimeStubs); + std::array fastRuntimeEnvs_; + std::array fastRuntimeOptimizers_; +}; +} // namespace kungfu +#endif // PANDA_RUNTIME_ECMASCRIPT_COMPILER_FASTPATH_OPTIMIZER_H \ No newline at end of file diff --git a/ecmascript/compiler/gate.cpp b/ecmascript/compiler/gate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..329cc2c2f8259482770e920c8155dd80b2a490ea --- /dev/null +++ b/ecmascript/compiler/gate.cpp @@ -0,0 +1,1296 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/compiler/gate.h" + +namespace kungfu { +constexpr size_t ONE_DEPEND = 1; +constexpr size_t MANY_DEPEND = 2; +constexpr size_t NO_DEPEND = 0; +// NOLINTNEXTLINE(readability-function-size) +Properties OpCode::GetProperties() const +{ +// general schema: [STATE]s + [DEPEND]s + [VALUE]s + [ROOT] +// GENERAL_STATE for any opcode match in +// {IF_TRUE, IF_FALSE, SWITCH_CASE, DEFAULT_CASE, MERGE, LOOP_BEGIN, STATE_ENTRY} +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define STATE(...) (std::make_pair(std::vector{__VA_ARGS__}, false)) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define VALUE(...) (std::make_pair(std::vector{__VA_ARGS__}, false)) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MANY_STATE(...) (std::make_pair(std::vector{__VA_ARGS__}, true)) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MANY_VALUE(...) (std::make_pair(std::vector{__VA_ARGS__}, true)) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define NO_STATE (std::nullopt) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define NO_VALUE (std::nullopt) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define NO_ROOT (std::nullopt) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GENERAL_STATE (NOP) + switch (this->op) { + // SHARED + case NOP: + case CIRCUIT_ROOT: + return {NOVALUE, NO_STATE, NO_DEPEND, NO_VALUE, NO_ROOT}; + case STATE_ENTRY: + case DEPEND_ENTRY: + case FRAMESTATE_ENTRY: + case RETURN_LIST: + case THROW_LIST: + case CONSTANT_LIST: + case ALLOCA_LIST: + case ARG_LIST: + return {NOVALUE, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(CIRCUIT_ROOT)}; + case RETURN: + return {NOVALUE, STATE(OpCode(GENERAL_STATE)), ONE_DEPEND, VALUE(ANYVALUE), OpCode(THROW_LIST)}; + case THROW: + return {NOVALUE, STATE(OpCode(GENERAL_STATE)), ONE_DEPEND, VALUE(JSValueCode()), OpCode(RETURN_LIST)}; + case ORDINARY_BLOCK: + return {NOVALUE, STATE(OpCode(GENERAL_STATE)), NO_DEPEND, NO_VALUE, NO_ROOT}; + case IF_BRANCH: + return {NOVALUE, STATE(OpCode(GENERAL_STATE)), NO_DEPEND, VALUE(INT1), NO_ROOT}; + case SWITCH_BRANCH: + return {NOVALUE, STATE(OpCode(GENERAL_STATE)), NO_DEPEND, VALUE(INT64), NO_ROOT}; + case IF_TRUE: + case IF_FALSE: + return {NOVALUE, STATE(OpCode(IF_BRANCH)), NO_DEPEND, NO_VALUE, NO_ROOT}; + case SWITCH_CASE: + case DEFAULT_CASE: + return {NOVALUE, STATE(OpCode(SWITCH_BRANCH)), NO_DEPEND, NO_VALUE, NO_ROOT}; + case MERGE: + return {NOVALUE, MANY_STATE(OpCode(GENERAL_STATE)), NO_DEPEND, NO_VALUE, NO_ROOT}; + case LOOP_BEGIN: + return {NOVALUE, STATE(OpCode(GENERAL_STATE), OpCode(LOOP_BACK)), NO_DEPEND, NO_VALUE, NO_ROOT}; + case LOOP_BACK: + return {NOVALUE, STATE(OpCode(GENERAL_STATE)), NO_DEPEND, NO_VALUE, NO_ROOT}; + case VALUE_SELECTOR_JS: + return {JSValueCode(), STATE(OpCode(GENERAL_STATE)), NO_DEPEND, MANY_VALUE(JSValueCode()), NO_ROOT}; + case VALUE_SELECTOR_INT1: + return {INT1, STATE(OpCode(GENERAL_STATE)), NO_DEPEND, MANY_VALUE(INT1), NO_ROOT}; + case VALUE_SELECTOR_INT8: + return {INT8, STATE(OpCode(GENERAL_STATE)), NO_DEPEND, MANY_VALUE(INT8), NO_ROOT}; + case VALUE_SELECTOR_INT16: + return {INT16, STATE(OpCode(GENERAL_STATE)), NO_DEPEND, MANY_VALUE(INT16), NO_ROOT}; + case VALUE_SELECTOR_INT32: + return {INT32, STATE(OpCode(GENERAL_STATE)), NO_DEPEND, MANY_VALUE(INT32), NO_ROOT}; + case VALUE_SELECTOR_INT64: + return {INT64, STATE(OpCode(GENERAL_STATE)), NO_DEPEND, MANY_VALUE(INT64), NO_ROOT}; + case VALUE_SELECTOR_FLOAT32: + return {FLOAT32, STATE(OpCode(GENERAL_STATE)), NO_DEPEND, MANY_VALUE(FLOAT32), NO_ROOT}; + case VALUE_SELECTOR_FLOAT64: + return {FLOAT64, STATE(OpCode(GENERAL_STATE)), NO_DEPEND, MANY_VALUE(FLOAT64), NO_ROOT}; + case DEPEND_SELECTOR: + return {NOVALUE, STATE(OpCode(GENERAL_STATE)), MANY_DEPEND, NO_VALUE, NO_ROOT}; + case DEPEND_RELAY: + return {NOVALUE, STATE(OpCode(GENERAL_STATE)), ONE_DEPEND, NO_VALUE, NO_ROOT}; + case DEPEND_AND: + return {NOVALUE, NO_STATE, MANY_DEPEND, NO_VALUE, NO_ROOT}; + // High Level IR + case JS_CALL: + return {JSValueCode(), NO_STATE, ONE_DEPEND, MANY_VALUE(ANYVALUE), NO_ROOT}; + case JS_CONSTANT: + return {JSValueCode(), NO_STATE, NO_DEPEND, NO_VALUE, OpCode(CONSTANT_LIST)}; + case JS_ARG: + return {JSValueCode(), NO_STATE, NO_DEPEND, NO_VALUE, OpCode(ARG_LIST)}; + case JS_ADD: + case JS_SUB: + case JS_MUL: + case JS_EXP: + case JS_DIV: + case JS_MOD: + case JS_AND: + case JS_XOR: + case JS_OR: + case JS_LSL: + case JS_LSR: + case JS_ASR: + case JS_LOGIC_AND: + case JS_LOGIC_OR: + case JS_LT: + case JS_LE: + case JS_GT: + case JS_GE: + case JS_EQ: + case JS_NE: + case JS_STRICT_EQ: + case JS_STRICT_NE: + return {JSValueCode(), NO_STATE, NO_DEPEND, VALUE(JSValueCode(), JSValueCode()), NO_ROOT}; + case JS_LOGIC_NOT: + return {JSValueCode(), NO_STATE, NO_DEPEND, VALUE(JSValueCode()), NO_ROOT}; + // Middle Level IR + case CALL: + return {NOVALUE, NO_STATE, ONE_DEPEND, MANY_VALUE(PtrValueCode(), ANYVALUE), NO_ROOT}; + case INT1_CALL: + return {INT1, NO_STATE, ONE_DEPEND, MANY_VALUE(PtrValueCode(), ANYVALUE), NO_ROOT}; + case INT8_CALL: + return {INT8, NO_STATE, ONE_DEPEND, MANY_VALUE(PtrValueCode(), ANYVALUE), NO_ROOT}; + case INT16_CALL: + return {INT16, NO_STATE, ONE_DEPEND, MANY_VALUE(PtrValueCode(), ANYVALUE), NO_ROOT}; + case INT32_CALL: + return {INT32, NO_STATE, ONE_DEPEND, MANY_VALUE(PtrValueCode(), ANYVALUE), NO_ROOT}; + case INT64_CALL: + return {INT64, NO_STATE, ONE_DEPEND, MANY_VALUE(PtrValueCode(), ANYVALUE), NO_ROOT}; + case FLOAT32_CALL: + return {FLOAT32, NO_STATE, ONE_DEPEND, MANY_VALUE(PtrValueCode(), ANYVALUE), NO_ROOT}; + case FLOAT64_CALL: + return {FLOAT64, NO_STATE, ONE_DEPEND, MANY_VALUE(PtrValueCode(), ANYVALUE), NO_ROOT}; + case ALLOCA: + return {PtrValueCode(), NO_STATE, NO_DEPEND, NO_VALUE, OpCode(ALLOCA_LIST)}; + case INT1_ARG: + return {INT1, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(ARG_LIST)}; + case INT8_ARG: + return {INT8, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(ARG_LIST)}; + case INT16_ARG: + return {INT16, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(ARG_LIST)}; + case INT32_ARG: + return {INT32, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(ARG_LIST)}; + case INT64_ARG: + return {INT64, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(ARG_LIST)}; + case FLOAT32_ARG: + return {FLOAT32, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(ARG_LIST)}; + case FLOAT64_ARG: + return {FLOAT64, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(ARG_LIST)}; + case MUTABLE_DATA: + case CONST_DATA: + return {PtrValueCode(), NO_STATE, NO_DEPEND, NO_VALUE, OpCode(CONSTANT_LIST)}; + case INT1_CONSTANT: + return {INT1, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(CONSTANT_LIST)}; + case INT8_CONSTANT: + return {INT8, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(CONSTANT_LIST)}; + case INT16_CONSTANT: + return {INT16, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(CONSTANT_LIST)}; + case INT32_CONSTANT: + return {INT32, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(CONSTANT_LIST)}; + case INT64_CONSTANT: + return {INT64, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(CONSTANT_LIST)}; + case FLOAT32_CONSTANT: + return {FLOAT32, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(CONSTANT_LIST)}; + case FLOAT64_CONSTANT: + return {FLOAT64, NO_STATE, NO_DEPEND, NO_VALUE, OpCode(CONSTANT_LIST)}; + case ZEXT_INT32_TO_INT64: + return {INT64, NO_STATE, NO_DEPEND, VALUE(INT32), NO_ROOT}; + case ZEXT_INT1_TO_INT32: + return {INT32, NO_STATE, NO_DEPEND, VALUE(INT1), NO_ROOT}; + case ZEXT_INT1_TO_INT64: + return {INT64, NO_STATE, NO_DEPEND, VALUE(INT1), NO_ROOT}; + case SEXT_INT32_TO_INT64: + return {INT64, NO_STATE, NO_DEPEND, VALUE(INT32), NO_ROOT}; + case SEXT_INT1_TO_INT32: + return {INT32, NO_STATE, NO_DEPEND, VALUE(INT1), NO_ROOT}; + case SEXT_INT1_TO_INT64: + return {INT64, NO_STATE, NO_DEPEND, VALUE(INT1), NO_ROOT}; + case TRUNC_INT64_TO_INT32: + return {INT32, NO_STATE, NO_DEPEND, VALUE(INT64), NO_ROOT}; + case TRUNC_INT64_TO_INT1: + return {INT1, NO_STATE, NO_DEPEND, VALUE(INT64), NO_ROOT}; + case TRUNC_INT32_TO_INT1: + return {INT1, NO_STATE, NO_DEPEND, VALUE(INT32), NO_ROOT}; + case INT32_REV: + return {INT32, NO_STATE, NO_DEPEND, VALUE(INT32), NO_ROOT}; + case INT32_ADD: + case INT32_SUB: + case INT32_MUL: + case INT32_EXP: + case INT32_SDIV: + case INT32_SMOD: + case INT32_UDIV: + case INT32_UMOD: + case INT32_AND: + case INT32_XOR: + case INT32_OR: + case INT32_LSL: + case INT32_LSR: + case INT32_ASR: + return {INT32, NO_STATE, NO_DEPEND, VALUE(INT32, INT32), NO_ROOT}; + case INT32_SLT: + case INT32_SLE: + case INT32_SGT: + case INT32_SGE: + case INT32_ULT: + case INT32_ULE: + case INT32_UGT: + case INT32_UGE: + case INT32_EQ: + case INT32_NE: + return {INT1, NO_STATE, NO_DEPEND, VALUE(INT32, INT32), NO_ROOT}; + case INT64_REV: + return {INT64, NO_STATE, NO_DEPEND, VALUE(INT64), NO_ROOT}; + case INT64_ADD: + case INT64_SUB: + case INT64_MUL: + case INT64_EXP: + case INT64_SDIV: + case INT64_SMOD: + case INT64_UDIV: + case INT64_UMOD: + case INT64_AND: + case INT64_XOR: + case INT64_OR: + case INT64_LSL: + case INT64_LSR: + case INT64_ASR: + return {INT64, NO_STATE, NO_DEPEND, VALUE(INT64, INT64), NO_ROOT}; + case INT64_SLT: + case INT64_SLE: + case INT64_SGT: + case INT64_SGE: + case INT64_ULT: + case INT64_ULE: + case INT64_UGT: + case INT64_UGE: + case INT64_EQ: + case INT64_NE: + return {INT1, NO_STATE, NO_DEPEND, VALUE(INT64, INT64), NO_ROOT}; + case FLOAT64_ADD: + case FLOAT64_SUB: + case FLOAT64_MUL: + case FLOAT64_DIV: + case FLOAT64_EXP: + return {FLOAT64, NO_STATE, NO_DEPEND, VALUE(FLOAT64, FLOAT64), NO_ROOT}; + case FLOAT64_EQ: + return {INT1, NO_STATE, NO_DEPEND, VALUE(FLOAT64, FLOAT64), NO_ROOT}; + case INT8_LOAD: + return {INT8, NO_STATE, ONE_DEPEND, VALUE(PtrValueCode()), NO_ROOT}; + case INT16_LOAD: + return {INT16, NO_STATE, ONE_DEPEND, VALUE(PtrValueCode()), NO_ROOT}; + case INT32_LOAD: + return {INT32, NO_STATE, ONE_DEPEND, VALUE(PtrValueCode()), NO_ROOT}; + case INT64_LOAD: + return {INT64, NO_STATE, ONE_DEPEND, VALUE(PtrValueCode()), NO_ROOT}; + case FLOAT32_LOAD: + return {FLOAT32, NO_STATE, ONE_DEPEND, VALUE(PtrValueCode()), NO_ROOT}; + case FLOAT64_LOAD: + return {FLOAT64, NO_STATE, ONE_DEPEND, VALUE(PtrValueCode()), NO_ROOT}; + case INT8_STORE: + return {NOVALUE, NO_STATE, ONE_DEPEND, VALUE(INT8, PtrValueCode()), NO_ROOT}; + case INT16_STORE: + return {NOVALUE, NO_STATE, ONE_DEPEND, VALUE(INT16, PtrValueCode()), NO_ROOT}; + case INT32_STORE: + return {NOVALUE, NO_STATE, ONE_DEPEND, VALUE(INT32, PtrValueCode()), NO_ROOT}; + case INT64_STORE: + return {NOVALUE, NO_STATE, ONE_DEPEND, VALUE(INT64, PtrValueCode()), NO_ROOT}; + case FLOAT32_STORE: + return {NOVALUE, NO_STATE, ONE_DEPEND, VALUE(FLOAT32, PtrValueCode()), NO_ROOT}; + case FLOAT64_STORE: + return {NOVALUE, NO_STATE, ONE_DEPEND, VALUE(FLOAT64, PtrValueCode()), NO_ROOT}; + case INT32_TO_FLOAT64: + return {FLOAT64, NO_STATE, NO_DEPEND, VALUE(INT32), NO_ROOT}; + case INT64_TO_FLOAT64: + return {FLOAT64, NO_STATE, NO_DEPEND, VALUE(INT64), NO_ROOT}; + case FLOAT64_TO_INT64: + return {INT64, NO_STATE, NO_DEPEND, VALUE(FLOAT64), NO_ROOT}; + default: + std::cerr << "Please complete OpCode properties (OpCode=" << this->op << ")" << std::endl; + UNREACHABLE(); + } +#undef STATE +#undef VALUE +#undef MANY_STATE +#undef MANY_VALUE +#undef NO_STATE +#undef NO_VALUE +#undef NO_ROOT +#undef GENERAL_STATE +} + +std::string OpCode::Str() const +{ + const std::map strMap = { + // SHARED + {NOP, "NOP"}, + {CIRCUIT_ROOT, "CIRCUIT_ROOT"}, + {STATE_ENTRY, "STATE_ENTRY"}, + {DEPEND_ENTRY, "DEPEND_ENTRY"}, + {FRAMESTATE_ENTRY, "FRAMESTATE_ENTRY"}, + {RETURN_LIST, "RETURN_LIST"}, + {THROW_LIST, "THROW_LIST"}, + {CONSTANT_LIST, "CONSTANT_LIST"}, + {ALLOCA_LIST, "ALLOCA_LIST"}, + {ARG_LIST, "ARG_LIST"}, + {RETURN, "RETURN"}, + {THROW, "THROW"}, + {ORDINARY_BLOCK, "ORDINARY_BLOCK"}, + {IF_BRANCH, "IF_BRANCH"}, + {SWITCH_BRANCH, "SWITCH_BRANCH"}, + {IF_TRUE, "IF_TRUE"}, + {IF_FALSE, "IF_FALSE"}, + {SWITCH_CASE, "SWITCH_CASE"}, + {DEFAULT_CASE, "DEFAULT_CASE"}, + {MERGE, "MERGE"}, + {LOOP_BEGIN, "LOOP_BEGIN"}, + {LOOP_BACK, "LOOP_BACK"}, + {VALUE_SELECTOR_JS, "VALUE_SELECTOR_JS"}, + {VALUE_SELECTOR_INT1, "VALUE_SELECTOR_INT1"}, + {VALUE_SELECTOR_INT8, "VALUE_SELECTOR_INT8"}, + {VALUE_SELECTOR_INT16, "VALUE_SELECTOR_INT16"}, + {VALUE_SELECTOR_INT32, "VALUE_SELECTOR_INT32"}, + {VALUE_SELECTOR_INT64, "VALUE_SELECTOR_INT64"}, + {VALUE_SELECTOR_FLOAT32, "VALUE_SELECTOR_FLOAT32"}, + {VALUE_SELECTOR_FLOAT64, "VALUE_SELECTOR_FLOAT64"}, + {DEPEND_SELECTOR, "DEPEND_SELECTOR"}, + {DEPEND_RELAY, "DEPEND_RELAY"}, + {DEPEND_AND, "DEPEND_AND"}, + // High Level IR + {JS_CALL, "JS_CALL"}, + {JS_CONSTANT, "JS_CONSTANT"}, + {JS_ARG, "JS_ARG"}, + {JS_ADD, "JS_ADD"}, + {JS_SUB, "JS_SUB"}, + {JS_MUL, "JS_MUL"}, + {JS_EXP, "JS_EXP"}, + {JS_DIV, "JS_DIV"}, + {JS_MOD, "JS_MOD"}, + {JS_AND, "JS_AND"}, + {JS_XOR, "JS_XOR"}, + {JS_OR, "JS_OR"}, + {JS_LSL, "JS_LSL"}, + {JS_LSR, "JS_LSR"}, + {JS_ASR, "JS_ASR"}, + {JS_LOGIC_AND, "JS_LOGIC_AND"}, + {JS_LOGIC_OR, "JS_LOGIC_OR"}, + {JS_LT, "JS_LT"}, + {JS_LE, "JS_LE"}, + {JS_GT, "JS_GT"}, + {JS_GE, "JS_GE"}, + {JS_EQ, "JS_EQ"}, + {JS_NE, "JS_NE"}, + {JS_STRICT_EQ, "JS_STRICT_EQ"}, + {JS_STRICT_NE, "JS_STRICT_NE"}, + {JS_LOGIC_NOT, "JS_LOGIC_NOT"}, + // Middle Level IR + {CALL, "CALL"}, + {INT1_CALL, "INT1_CALL"}, + {INT8_CALL, "INT8_CALL"}, + {INT16_CALL, "INT16_CALL"}, + {INT32_CALL, "INT32_CALL"}, + {INT64_CALL, "INT64_CALL"}, + {FLOAT32_CALL, "FLOAT32_CALL"}, + {FLOAT64_CALL, "FLOAT64_CALL"}, + {ALLOCA, "ALLOCA"}, + {INT1_ARG, "INT1_ARG"}, + {INT8_ARG, "INT8_ARG"}, + {INT16_ARG, "INT16_ARG"}, + {INT32_ARG, "INT32_ARG"}, + {INT64_ARG, "INT64_ARG"}, + {FLOAT32_ARG, "FLOAT32_ARG"}, + {FLOAT64_ARG, "FLOAT64_ARG"}, + {MUTABLE_DATA, "MUTABLE_DATA"}, + {CONST_DATA, "CONST_DATA"}, + {INT1_CONSTANT, "INT1_CONSTANT"}, + {INT8_CONSTANT, "INT8_CONSTANT"}, + {INT16_CONSTANT, "INT16_CONSTANT"}, + {INT32_CONSTANT, "INT32_CONSTANT"}, + {INT64_CONSTANT, "INT64_CONSTANT"}, + {FLOAT32_CONSTANT, "FLOAT32_CONSTANT"}, + {FLOAT64_CONSTANT, "FLOAT64_CONSTANT"}, + {ZEXT_INT32_TO_INT64, "ZEXT_INT32_TO_INT64"}, + {ZEXT_INT1_TO_INT32, "ZEXT_INT1_TO_INT32"}, + {ZEXT_INT1_TO_INT64, "ZEXT_INT1_TO_INT64"}, + {SEXT_INT32_TO_INT64, "SEXT_INT32_TO_INT64"}, + {SEXT_INT1_TO_INT32, "SEXT_INT1_TO_INT32"}, + {SEXT_INT1_TO_INT64, "SEXT_INT1_TO_INT64"}, + {TRUNC_INT64_TO_INT32, "TRUNC_INT64_TO_INT32"}, + {TRUNC_INT64_TO_INT1, "TRUNC_INT64_TO_INT1"}, + {TRUNC_INT32_TO_INT1, "TRUNC_INT32_TO_INT1"}, + {INT32_REV, "INT32_REV"}, + {INT32_ADD, "INT32_ADD"}, + {INT32_SUB, "INT32_SUB"}, + {INT32_MUL, "INT32_MUL"}, + {INT32_EXP, "INT32_EXP"}, + {INT32_SDIV, "INT32_SDIV"}, + {INT32_SMOD, "INT32_SMOD"}, + {INT32_UDIV, "INT32_UDIV"}, + {INT32_UMOD, "INT32_UMOD"}, + {INT32_AND, "INT32_AND"}, + {INT32_XOR, "INT32_XOR"}, + {INT32_OR, "INT32_OR"}, + {INT32_LSL, "INT32_LSL"}, + {INT32_LSR, "INT32_LSR"}, + {INT32_ASR, "INT32_ASR"}, + {INT32_SLT, "INT32_SLT"}, + {INT32_SLE, "INT32_SLE"}, + {INT32_SGT, "INT32_SGT"}, + {INT32_SGE, "INT32_SGE"}, + {INT32_ULT, "INT32_ULT"}, + {INT32_ULE, "INT32_ULE"}, + {INT32_UGT, "INT32_UGT"}, + {INT32_UGE, "INT32_UGE"}, + {INT32_EQ, "INT32_EQ"}, + {INT32_NE, "INT32_NE"}, + {INT64_ADD, "INT64_ADD"}, + {INT64_SUB, "INT64_SUB"}, + {INT64_MUL, "INT64_MUL"}, + {INT64_EXP, "INT64_EXP"}, + {INT64_SDIV, "INT64_SDIV"}, + {INT64_SMOD, "INT64_SMOD"}, + {INT64_UDIV, "INT64_UDIV"}, + {INT64_UMOD, "INT64_UMOD"}, + {INT64_AND, "INT64_AND"}, + {INT64_XOR, "INT64_XOR"}, + {INT64_OR, "INT64_OR"}, + {INT64_LSL, "INT64_LSL"}, + {INT64_LSR, "INT64_LSR"}, + {INT64_ASR, "INT64_ASR"}, + {INT64_SLT, "INT64_SLT"}, + {INT64_SLE, "INT64_SLE"}, + {INT64_SGT, "INT64_SGT"}, + {INT64_SGE, "INT64_SGE"}, + {INT64_ULT, "INT64_ULT"}, + {INT64_ULE, "INT64_ULE"}, + {INT64_UGT, "INT64_UGT"}, + {INT64_UGE, "INT64_UGE"}, + {INT64_EQ, "INT64_EQ"}, + {INT64_NE, "INT64_NE"}, + {INT64_REV, "INT64_REV"}, + {FLOAT64_ADD, "FLOAT64_ADD"}, + {FLOAT64_SUB, "FLOAT64_SUB"}, + {FLOAT64_MUL, "FLOAT64_MUL"}, + {FLOAT64_DIV, "FLOAT64_DIV"}, + {FLOAT64_EXP, "FLOAT64_EXP"}, + {FLOAT64_EQ, "FLOAT64_EQ"}, + {INT8_LOAD, "INT8_LOAD"}, + {INT16_LOAD, "INT16_LOAD"}, + {INT32_LOAD, "INT32_LOAD"}, + {INT64_LOAD, "INT64_LOAD"}, + {FLOAT32_LOAD, "FLOAT32_LOAD"}, + {FLOAT64_LOAD, "FLOAT64_LOAD"}, + {INT8_STORE, "INT8_STORE"}, + {INT16_STORE, "INT16_STORE"}, + {INT32_STORE, "INT32_STORE"}, + {INT64_STORE, "INT64_STORE"}, + {FLOAT32_STORE, "FLOAT32_STORE"}, + {FLOAT64_STORE, "FLOAT64_STORE"}, + {INT32_TO_FLOAT64, "INT32_TO_FLOAT64"}, + {INT64_TO_FLOAT64, "INT64_TO_FLOAT64"}, + {FLOAT64_TO_INT64, "FLOAT64_TO_INT64"}, + }; + if (strMap.count(this->op) > 0) { + return strMap.at(this->op); + } + return "OP-" + std::to_string(this->op); +} +// 4 : 4 means that there are 4 args in total +std::array OpCode::GetOpCodeNumInsArray(BitField bitfield) const +{ + const size_t manyDepend = 2; + auto properties = this->GetProperties(); + auto stateProp = properties.statesIn; + auto dependProp = properties.dependsIn; + auto valueProp = properties.valuesIn; + auto rootProp = properties.states; + size_t stateSize = stateProp.has_value() ? (stateProp->second ? bitfield : stateProp->first.size()) : 0; + size_t dependSize = (dependProp == manyDepend) ? bitfield : dependProp; + size_t valueSize = valueProp.has_value() ? (valueProp->second ? bitfield : valueProp->first.size()) : 0; + size_t rootSize = rootProp.has_value() ? 1 : 0; + return {stateSize, dependSize, valueSize, rootSize}; +} + +size_t OpCode::GetOpCodeNumIns(BitField bitfield) const +{ + auto numInsArray = this->GetOpCodeNumInsArray(bitfield); + // 2 : 2 means the third element. + // 3 : 3 means the fourth element. + return numInsArray[0] + numInsArray[1] + numInsArray[2] + numInsArray[3]; +} + +ValueCode OpCode::GetValueCode() const +{ + return this->GetProperties().returnValue; +} + +ValueCode OpCode::GetInValueCode(BitField bitfield, size_t idx) const +{ + auto numInsArray = this->GetOpCodeNumInsArray(bitfield); + auto valueProp = this->GetProperties().valuesIn; + idx -= numInsArray[0]; + idx -= numInsArray[1]; + ASSERT(valueProp.has_value()); + if (valueProp->second) { + return valueProp->first.at(std::min(idx, valueProp->first.size() - 1)); + } + return valueProp->first.at(idx); +} + +OpCode OpCode::GetInStateCode(size_t idx) const +{ + auto stateProp = this->GetProperties().statesIn; + ASSERT(stateProp.has_value()); + if (stateProp->second) { + return stateProp->first.at(std::min(idx, stateProp->first.size() - 1)); + } + return stateProp->first.at(idx); +} + +std::string ValueCodeToStr(ValueCode valueCode) +{ + switch (valueCode) { + case NOVALUE: + return "NOVALUE"; + case ANYVALUE: + return "ANYVALUE"; + case INT1: + return "INT1"; + case INT8: + return "INT8"; + case INT16: + return "INT16"; + case INT32: + return "INT32"; + case INT64: + return "INT64"; + case FLOAT32: + return "FLOAT32"; + case FLOAT64: + return "FLOAT64"; + default: + return "???"; + } +} + +std::optional> Gate::CheckNullInput() const +{ + const auto numIns = this->GetNumIns(); + for (size_t idx = 0; idx < numIns; idx++) { + if (this->IsInGateNull(idx)) { + return std::make_pair("In list contains null", idx); + } + } + return std::nullopt; +} + +std::optional> Gate::CheckStateInput() const +{ + const auto numInsArray = this->GetOpCode().GetOpCodeNumInsArray(this->GetBitField()); + size_t stateStart = 0; + size_t stateEnd = numInsArray[0]; + for (size_t idx = stateStart; idx < stateEnd; idx++) { + auto stateProp = this->GetOpCode().GetProperties().statesIn; + ASSERT(stateProp.has_value()); + auto expectedIn = this->GetOpCode().GetInStateCode(idx); + auto actualIn = this->GetInGateConst(idx)->GetOpCode(); + if (expectedIn == OpCode::NOP) { // general + if (!actualIn.IsGeneralState()) { + return std::make_pair( + "State input does not match (expected: actual:" + actualIn.Str() + ")", idx); + } + } else { + if (expectedIn != actualIn) { + return std::make_pair( + "State input does not match (expected:" + expectedIn.Str() + " actual:" + actualIn.Str() + ")", + idx); + } + } + } + return std::nullopt; +} + +std::optional> Gate::CheckValueInput() const +{ + const auto numInsArray = this->GetOpCode().GetOpCodeNumInsArray(this->GetBitField()); + size_t valueStart = numInsArray[0] + numInsArray[1]; + size_t valueEnd = numInsArray[0] + numInsArray[1] + numInsArray[2]; // 2 : 2 means the third element. + for (size_t idx = valueStart; idx < valueEnd; idx++) { + auto expectedIn = this->GetOpCode().GetInValueCode(this->GetBitField(), idx); + auto actualIn = this->GetInGateConst(idx)->GetOpCode().GetValueCode(); + if ((expectedIn != actualIn) && (expectedIn != ANYVALUE)) { + return std::make_pair("Value input does not match (expected: " + ValueCodeToStr(expectedIn) + + " actual: " + ValueCodeToStr(actualIn) + ")", + idx); + } + } + return std::nullopt; +} + +std::optional> Gate::CheckDependInput() const +{ + const auto numInsArray = this->GetOpCode().GetOpCodeNumInsArray(this->GetBitField()); + size_t dependStart = numInsArray[0]; + size_t dependEnd = dependStart + numInsArray[1]; + for (size_t idx = dependStart; idx < dependEnd; idx++) { + if (this->GetInGateConst(idx)->GetNumInsArray()[1] == 0 && + this->GetInGateConst(idx)->GetOpCode() != OpCode::DEPEND_ENTRY) { + return std::make_pair("Depend input is side-effect free", idx); + } + } + return std::nullopt; +} + +std::optional> Gate::CheckStateOutput() const +{ + if (this->GetOpCode().IsState()) { + size_t cnt = 0; + const Gate *curGate = this; + if (!curGate->IsFirstOutNull()) { + const Out *curOut = curGate->GetFirstOutConst(); + if (curOut->GetGateConst()->GetOpCode().IsState()) { + cnt++; + } + while (!curOut->IsNextOutNull()) { + curOut = curOut->GetNextOutConst(); + if (curOut->GetGateConst()->GetOpCode().IsState()) { + cnt++; + } + } + } + size_t expected = 0; + bool needCheck = true; + if (this->GetOpCode().IsTerminalState()) { + expected = 0; + } else if (this->GetOpCode() == OpCode::IF_BRANCH) { + expected = 2; // 2: expected number of state out branches + } else if (this->GetOpCode() == OpCode::SWITCH_BRANCH) { + needCheck = false; + } else { + expected = 1; + } + if (needCheck && cnt != expected) { + return std::make_pair("Number of state out branches is not valid (expected:" + std::to_string(expected) + + " actual:" + std::to_string(cnt) + ")", + -1); + } + } + return std::nullopt; +} + +std::optional> Gate::CheckBranchOutput() const +{ + std::map, size_t> setOfOps; + if (this->GetOpCode() == OpCode::IF_BRANCH || this->GetOpCode() == OpCode::SWITCH_BRANCH) { + size_t cnt = 0; + const Gate *curGate = this; + if (!curGate->IsFirstOutNull()) { + const Out *curOut = curGate->GetFirstOutConst(); + if (curOut->GetGateConst()->GetOpCode().IsState()) { + setOfOps[{curOut->GetGateConst()->GetOpCode(), curOut->GetGateConst()->GetBitField()}]++; + cnt++; + } + while (!curOut->IsNextOutNull()) { + curOut = curOut->GetNextOutConst(); + if (curOut->GetGateConst()->GetOpCode().IsState()) { + setOfOps[{curOut->GetGateConst()->GetOpCode(), curOut->GetGateConst()->GetBitField()}]++; + cnt++; + } + } + } + if (setOfOps.size() != cnt) { + return std::make_pair("Duplicate state out branches", -1); + } + } + return std::nullopt; +} + +std::optional> Gate::CheckNOP() const +{ + if (this->GetOpCode() == OpCode::NOP) { + if (!this->IsFirstOutNull()) { + return std::make_pair("NOP gate used by other gates", -1); + } + } + return std::nullopt; +} + +std::optional> Gate::CheckSelector() const +{ + if (this->GetOpCode() == OpCode::VALUE_SELECTOR_JS || + (OpCode::VALUE_SELECTOR_INT1 <= this->GetOpCode() && this->GetOpCode() <= OpCode::VALUE_SELECTOR_FLOAT64) || + this->GetOpCode() == OpCode::DEPEND_SELECTOR) { + auto stateOp = this->GetInGateConst(0)->GetOpCode(); + if (stateOp == OpCode::MERGE || stateOp == OpCode::LOOP_BEGIN) { + if (this->GetInGateConst(0)->GetNumIns() != this->GetNumIns() - 1) { + if (this->GetOpCode() == OpCode::DEPEND_SELECTOR) { + return std::make_pair("Number of depend flows does not match control flows (expected:" + + std::to_string(this->GetInGateConst(0)->GetNumIns()) + + " actual:" + std::to_string(this->GetNumIns() - 1) + ")", + -1); + } else { + return std::make_pair("Number of data flows does not match control flows (expected:" + + std::to_string(this->GetInGateConst(0)->GetNumIns()) + + " actual:" + std::to_string(this->GetNumIns() - 1) + ")", + -1); + } + } + } else { + return std::make_pair( + "State input does not match (expected:[MERGE|LOOP_BEGIN] actual:" + stateOp.Str() + ")", 0); + } + } + return std::nullopt; +} + +std::optional> Gate::CheckRelay() const +{ + if (this->GetOpCode() == OpCode::DEPEND_RELAY) { + auto stateOp = this->GetInGateConst(0)->GetOpCode(); + if (!(stateOp == OpCode::IF_TRUE || stateOp == OpCode::IF_FALSE || stateOp == OpCode::SWITCH_CASE || + stateOp == OpCode::DEFAULT_CASE)) { + return std::make_pair( + "State input does not match (expected:[IF_TRUE|IF_FALSE|SWITCH_CASE|DEFAULT_CASE] actual:" + + stateOp.Str() + ")", + 0); + } + } + return std::nullopt; +} + +std::optional> Gate::SpecialCheck() const +{ + { + auto ret = this->CheckNOP(); + if (ret.has_value()) { + return ret; + } + } + { + auto ret = this->CheckSelector(); + if (ret.has_value()) { + return ret; + } + } + { + auto ret = this->CheckRelay(); + if (ret.has_value()) { + return ret; + } + } + return std::nullopt; +} + +bool Gate::Verify() const +{ + std::string errorString; + size_t highlightIdx = -1; + bool failed = false; + { + auto ret = this->CheckNullInput(); + if (ret.has_value()) { + failed = true; + std::tie(errorString, highlightIdx) = ret.value(); + } + } + if (!failed) { + auto ret = this->CheckStateInput(); + if (ret.has_value()) { + failed = true; + std::tie(errorString, highlightIdx) = ret.value(); + } + } + if (!failed) { + auto ret = this->CheckValueInput(); + if (ret.has_value()) { + failed = true; + std::tie(errorString, highlightIdx) = ret.value(); + } + } + if (!failed) { + auto ret = this->CheckDependInput(); + if (ret.has_value()) { + failed = true; + std::tie(errorString, highlightIdx) = ret.value(); + } + } + if (!failed) { + auto ret = this->CheckStateOutput(); + if (ret.has_value()) { + failed = true; + std::tie(errorString, highlightIdx) = ret.value(); + } + } + if (!failed) { + auto ret = this->CheckBranchOutput(); + if (ret.has_value()) { + failed = true; + std::tie(errorString, highlightIdx) = ret.value(); + } + } + if (!failed) { + auto ret = this->SpecialCheck(); + if (ret.has_value()) { + failed = true; + std::tie(errorString, highlightIdx) = ret.value(); + } + } + if (failed) { + std::cerr << "[Verifier][Error] Gate level input list schema verify failed" << std::endl; + this->Print(true, highlightIdx); + std::cerr << "Note: " << errorString << std::endl; + } + return !failed; +} + +ValueCode JSValueCode() +{ + return ValueCode::INT64; +} + +ValueCode PtrValueCode() +{ +#ifdef PANDA_TARGET_AMD64 + return ValueCode::INT64; +#endif +#ifdef PANDA_TARGET_X86 + return ValueCode::INT32; +#endif +#ifdef PANDA_TARGET_ARM64 + return ValueCode::INT64; +#endif +#ifdef PANDA_TARGET_ARM32 + return ValueCode::INT32; +#endif +} + +size_t GetValueBits(ValueCode valueCode) +{ + switch (valueCode) { + case NOVALUE: + return 0; // 0: 0 means 0 bits + case INT1: + return 1; // 1: 1 means 1 bits + case INT8: + return 8; // 8: 8 means 8 bits + case INT16: + return 16; // 16: 16 means 16 bits + case INT32: + return 32; // 32: 32 means 32 bits + case INT64: + return 64; // 64: 64 means 64 bits + case FLOAT32: + return 32; // 32: 32 means 32 bits + case FLOAT64: + return 64; // 64: 64 means 64 bits + default: + UNREACHABLE(); + } +} + +size_t GetOpCodeNumIns(OpCode opcode, BitField bitfield) +{ + return opcode.GetOpCodeNumIns(bitfield); +} + +void Out::SetNextOut(const Out *ptr) +{ + this->nextOut = + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + static_cast((reinterpret_cast(ptr)) - (reinterpret_cast(this))); +} + +Out *Out::GetNextOut() +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast((reinterpret_cast(this)) + this->nextOut); +} + +const Out *Out::GetNextOutConst() const +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast((reinterpret_cast(this)) + this->nextOut); +} + +void Out::SetPrevOut(const Out *ptr) +{ + this->prevOut = + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + static_cast((reinterpret_cast(ptr)) - (reinterpret_cast(this))); +} + +Out *Out::GetPrevOut() +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast((reinterpret_cast(this)) + this->prevOut); +} + +const Out *Out::GetPrevOutConst() const +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast((reinterpret_cast(this)) + this->prevOut); +} + +void Out::SetIndex(OutIdx idx) +{ + this->idx = idx; +} + +OutIdx Out::GetIndex() const +{ + return this->idx; +} + +Gate *Out::GetGate() +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast(&this[this->idx + 1]); +} + +const Gate *Out::GetGateConst() const +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast(&this[this->idx + 1]); +} + +void Out::SetPrevOutNull() +{ + this->prevOut = 0; +} + +bool Out::IsPrevOutNull() const +{ + return this->prevOut == 0; +} + +void Out::SetNextOutNull() +{ + this->nextOut = 0; +} + +bool Out::IsNextOutNull() const +{ + return this->nextOut == 0; +} + +void In::SetGate(const Gate *ptr) +{ + this->gatePtr = + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + static_cast((reinterpret_cast(ptr)) - (reinterpret_cast(this))); +} + +Gate *In::GetGate() +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast((reinterpret_cast(this)) + this->gatePtr); +} + +const Gate *In::GetGateConst() const +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast((reinterpret_cast(this)) + this->gatePtr); +} + +void In::SetGateNull() +{ + this->gatePtr = 0; +} + +bool In::IsGateNull() const +{ + return this->gatePtr == 0; +} + +// NOLINTNEXTLINE(modernize-avoid-c-arrays) +Gate::Gate(GateId id, OpCode opcode, BitField bitfield, Gate *inList[], TypeCode type, MarkCode mark) + : id(id), opcode(opcode), type(type), stamp(1), mark(mark), bitfield(bitfield), firstOut(0) +{ + auto numIns = this->GetNumIns(); + for (size_t idx = 0; idx < numIns; idx++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto in = inList[idx]; + if (in == nullptr) { + this->GetIn(idx)->SetGateNull(); + } else { + this->NewIn(idx, in); + } + auto curOut = this->GetOut(idx); + curOut->SetIndex(idx); + } +} + +size_t Gate::GetOutListSize(size_t numIns) +{ + return numIns * sizeof(Out); +} + +size_t Gate::GetOutListSize() const +{ + return Gate::GetOutListSize(this->GetNumIns()); +} + +size_t Gate::GetInListSize(size_t numIns) +{ + return numIns * sizeof(In); +} + +size_t Gate::GetInListSize() const +{ + return Gate::GetInListSize(this->GetNumIns()); +} + +size_t Gate::GetGateSize(size_t numIns) +{ + return Gate::GetOutListSize(numIns) + Gate::GetInListSize(numIns) + sizeof(Gate); +} + +size_t Gate::GetGateSize() const +{ + return Gate::GetGateSize(this->GetNumIns()); +} + +void Gate::NewIn(size_t idx, Gate *in) +{ + this->GetIn(idx)->SetGate(in); + auto curOut = this->GetOut(idx); + if (in->IsFirstOutNull()) { + curOut->SetNextOutNull(); + } else { + curOut->SetNextOut(in->GetFirstOut()); + in->GetFirstOut()->SetPrevOut(curOut); + } + curOut->SetPrevOutNull(); + in->SetFirstOut(curOut); +} + +void Gate::ModifyIn(size_t idx, Gate *in) +{ + this->DeleteIn(idx); + this->NewIn(idx, in); +} + +void Gate::DeleteIn(size_t idx) +{ + if (!this->GetOut(idx)->IsNextOutNull() && !this->GetOut(idx)->IsPrevOutNull()) { + this->GetOut(idx)->GetPrevOut()->SetNextOut(this->GetOut(idx)->GetNextOut()); + this->GetOut(idx)->GetNextOut()->SetPrevOut(this->GetOut(idx)->GetPrevOut()); + } else if (this->GetOut(idx)->IsNextOutNull() && !this->GetOut(idx)->IsPrevOutNull()) { + this->GetOut(idx)->GetPrevOut()->SetNextOutNull(); + } else if (!this->GetOut(idx)->IsNextOutNull()) { // then this->GetOut(idx)->IsPrevOutNull() is true + this->GetIn(idx)->GetGate()->SetFirstOut(this->GetOut(idx)->GetNextOut()); + this->GetOut(idx)->GetNextOut()->SetPrevOutNull(); + } else { // only this out now + this->GetIn(idx)->GetGate()->SetFirstOutNull(); + } + this->GetIn(idx)->SetGateNull(); +} + +void Gate::DeleteGate() +{ + auto numIns = this->GetNumIns(); + for (size_t idx = 0; idx < numIns; idx++) { + this->DeleteIn(idx); + } + this->SetOpCode(OpCode(OpCode::NOP)); +} + +Out *Gate::GetOut(size_t idx) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return &reinterpret_cast(this)[-1 - idx]; +} + +Out *Gate::GetFirstOut() +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast((reinterpret_cast(this)) + this->firstOut); +} + +const Out *Gate::GetFirstOutConst() const +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast((reinterpret_cast(this)) + this->firstOut); +} + +void Gate::SetFirstOutNull() +{ + this->firstOut = 0; +} + +bool Gate::IsFirstOutNull() const +{ + return this->firstOut == 0; +} + +void Gate::SetFirstOut(const Out *firstOut) +{ + this->firstOut = + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + static_cast(reinterpret_cast(firstOut) - reinterpret_cast(this)); +} + +In *Gate::GetIn(size_t idx) +{ +#ifndef NDEBUG + if (idx >= this->GetNumIns()) { + std::cerr << std::dec << "Gate In access out-of-bound! (idx=" << idx << ")" << std::endl; + this->Print(); + ASSERT(false); + } +#endif + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return &reinterpret_cast(this + 1)[idx]; +} + +const In *Gate::GetInConst(size_t idx) const +{ +#ifndef NDEBUG + if (idx >= this->GetNumIns()) { + std::cerr << std::dec << "Gate In access out-of-bound! (idx=" << idx << ")" << std::endl; + this->Print(); + ASSERT(false); + } +#endif + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return &reinterpret_cast(this + 1)[idx]; +} + +Gate *Gate::GetInGate(size_t idx) +{ + return this->GetIn(idx)->GetGate(); +} + +const Gate *Gate::GetInGateConst(size_t idx) const +{ + return this->GetInConst(idx)->GetGateConst(); +} + +bool Gate::IsInGateNull(size_t idx) const +{ + return this->GetInConst(idx)->IsGateNull(); +} + +GateId Gate::GetId() const +{ + return id; +} + +OpCode Gate::GetOpCode() const +{ + return this->opcode; +} + +void Gate::SetOpCode(OpCode opcode) +{ + this->opcode = opcode; +} + +size_t Gate::GetNumIns() const +{ + return GetOpCodeNumIns(this->GetOpCode(), this->GetBitField()); +} + +std::array Gate::GetNumInsArray() const // 4 : 4 means that there are 4 args. +{ + return this->GetOpCode().GetOpCodeNumInsArray(this->GetBitField()); +} + +BitField Gate::GetBitField() const +{ + return this->bitfield; +} + +void Gate::SetBitField(BitField bitfield) +{ + this->bitfield = bitfield; +} + +void Gate::Print(bool inListPreview, size_t highlightIdx) const +{ + if (this->GetOpCode() != OpCode::NOP) { + std::cerr << std::dec << "(" + << "id=" << this->id << ", " + << "op=" << this->GetOpCode().Str() << ", " + << "bitfield=" << std::to_string(this->bitfield) << ", " + << "type=" << static_cast(this->type) << ", " + << "stamp=" << static_cast(this->stamp) << ", " + << "mark=" << static_cast(this->mark) << ", "; + std::cerr << "in=" + << "["; + for (size_t idx = 0; idx < this->GetNumIns(); idx++) { + std::cerr << std::dec << ((idx == 0) ? "" : " ") << ((idx == highlightIdx) ? "\033[4;31m" : "") + << ((this->IsInGateNull(idx) + ? "N" + : (std::to_string(this->GetInGateConst(idx)->GetId()) + + (inListPreview ? std::string(":" + this->GetInGateConst(idx)->GetOpCode().Str()) + : std::string(""))))) + << ((idx == highlightIdx) ? "\033[0m" : ""); + } + std::cerr << "]" + << ", "; + std::cerr << "out=" + << "["; + if (!this->IsFirstOutNull()) { + const Out *curOut = this->GetFirstOutConst(); + std::cerr << std::dec << "" + << std::to_string(curOut->GetGateConst()->GetId()) + + (inListPreview ? std::string(":" + curOut->GetGateConst()->GetOpCode().Str()) : std::string("")); + while (!curOut->IsNextOutNull()) { + curOut = curOut->GetNextOutConst(); + std::cerr << std::dec << " " + << std::to_string(curOut->GetGateConst()->GetId()) + + (inListPreview ? std::string(":" + curOut->GetGateConst()->GetOpCode().Str()) + : std::string("")); + } + } + std::cerr << "]" + << ")" << std::endl; + } +} + +MarkCode Gate::GetMark(TimeStamp stamp) const +{ + return (this->stamp == stamp) ? this->mark : MarkCode::EMPTY; +} + +void Gate::SetMark(MarkCode mark, TimeStamp stamp) +{ + this->stamp = stamp; + this->mark = mark; +} + +bool OpCode::IsRoot() const +{ + return (this->GetProperties().states == OpCode::CIRCUIT_ROOT) || (this->op == OpCode::CIRCUIT_ROOT); +} + +bool OpCode::IsProlog() const +{ + return (this->GetProperties().states == OpCode::ARG_LIST); +} + +bool OpCode::IsFixed() const +{ + return (this->GetOpCodeNumInsArray(1)[0] > 0) && + ((this->GetValueCode() != NOVALUE) || + ((this->GetOpCodeNumInsArray(1)[1] > 0) && (this->GetOpCodeNumInsArray(1)[2] == 0))); +} + +bool OpCode::IsSchedulable() const +{ + return (this->op != OpCode::NOP) && (!this->IsProlog()) && (!this->IsRoot()) && (!this->IsFixed()) && + (this->GetOpCodeNumInsArray(1)[0] == 0); +} + +bool OpCode::IsState() const +{ + return (this->op != OpCode::NOP) && (!this->IsProlog()) && (!this->IsRoot()) && (!this->IsFixed()) && + (this->GetOpCodeNumInsArray(1)[0] > 0); +} + +bool OpCode::IsGeneralState() const +{ + return ((this->op == OpCode::IF_TRUE) || (this->op == OpCode::IF_FALSE) || (this->op == OpCode::SWITCH_CASE) || + (this->op == OpCode::DEFAULT_CASE) || (this->op == OpCode::MERGE) || (this->op == OpCode::LOOP_BEGIN) || + (this->op == OpCode::ORDINARY_BLOCK) || (this->op == OpCode::STATE_ENTRY)); +} + +bool OpCode::IsTerminalState() const +{ + return ((this->op == OpCode::RETURN) || (this->op == OpCode::THROW)); +} + +bool OpCode::IsCFGMerge() const +{ + return (this->op == OpCode::MERGE) || (this->op == OpCode::LOOP_BEGIN); +} +} // namespace kungfu \ No newline at end of file diff --git a/ecmascript/compiler/gate.h b/ecmascript/compiler/gate.h new file mode 100644 index 0000000000000000000000000000000000000000..9b1b1255c80d691b354932e1d6adaa17aec4be36 --- /dev/null +++ b/ecmascript/compiler/gate.h @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_GATE_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_GATE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libpandabase/macros.h" +#include "ecmascript/compiler/type.h" + +namespace kungfu { +using GateId = uint32_t; +using GateOp = uint8_t; +using GateMark = uint8_t; +using TimeStamp = uint8_t; +using AddrShift = int32_t; +using BitField = uint64_t; +using OutIdx = uint32_t; +class Gate; + +enum ValueCode { + NOVALUE, + ANYVALUE, + INT1, + INT8, + INT16, + INT32, + INT64, + FLOAT32, + FLOAT64, +}; + +std::string ValueCodeToStr(ValueCode valueCode); + +using Properties = struct Properties; + +class OpCode { +public: + enum Op : GateOp { + // SHARED + NOP, + CIRCUIT_ROOT, + STATE_ENTRY, + DEPEND_ENTRY, + FRAMESTATE_ENTRY, + RETURN_LIST, + THROW_LIST, + CONSTANT_LIST, + ALLOCA_LIST, + ARG_LIST, + RETURN, + THROW, + ORDINARY_BLOCK, + IF_BRANCH, + SWITCH_BRANCH, + IF_TRUE, + IF_FALSE, + SWITCH_CASE, + DEFAULT_CASE, + MERGE, + LOOP_BEGIN, + LOOP_BACK, + VALUE_SELECTOR_JS, + VALUE_SELECTOR_INT1, + VALUE_SELECTOR_INT8, + VALUE_SELECTOR_INT16, + VALUE_SELECTOR_INT32, + VALUE_SELECTOR_INT64, + VALUE_SELECTOR_FLOAT32, + VALUE_SELECTOR_FLOAT64, + DEPEND_SELECTOR, + DEPEND_RELAY, + DEPEND_AND, + // High Level IR + JS_CALL, + JS_CONSTANT, + JS_ARG, + JS_ADD, + JS_SUB, + JS_MUL, + JS_EXP, + JS_DIV, + JS_MOD, + JS_AND, + JS_XOR, + JS_OR, + JS_LSL, + JS_LSR, + JS_ASR, + JS_LOGIC_AND, + JS_LOGIC_OR, + JS_LT, + JS_LE, + JS_GT, + JS_GE, + JS_EQ, + JS_NE, + JS_STRICT_EQ, + JS_STRICT_NE, + JS_LOGIC_NOT, + // Middle Level IR + CALL, + INT1_CALL, + INT8_CALL, + INT16_CALL, + INT32_CALL, + INT64_CALL, + FLOAT32_CALL, + FLOAT64_CALL, + ALLOCA, + INT1_ARG, + INT8_ARG, + INT16_ARG, + INT32_ARG, + INT64_ARG, + FLOAT32_ARG, + FLOAT64_ARG, + MUTABLE_DATA, + CONST_DATA, + INT1_CONSTANT, + INT8_CONSTANT, + INT16_CONSTANT, + INT32_CONSTANT, + INT64_CONSTANT, + FLOAT32_CONSTANT, + FLOAT64_CONSTANT, + ZEXT_INT32_TO_INT64, + ZEXT_INT1_TO_INT32, + ZEXT_INT1_TO_INT64, + SEXT_INT32_TO_INT64, + SEXT_INT1_TO_INT32, + SEXT_INT1_TO_INT64, + TRUNC_INT64_TO_INT32, + TRUNC_INT64_TO_INT1, + TRUNC_INT32_TO_INT1, + INT32_REV, + INT32_ADD, + INT32_SUB, + INT32_MUL, + INT32_EXP, + INT32_SDIV, + INT32_SMOD, + INT32_UDIV, + INT32_UMOD, + INT32_AND, + INT32_XOR, + INT32_OR, + INT32_LSL, + INT32_LSR, + INT32_ASR, + INT32_SLT, + INT32_SLE, + INT32_SGT, + INT32_SGE, + INT32_ULT, + INT32_ULE, + INT32_UGT, + INT32_UGE, + INT32_EQ, + INT32_NE, + INT64_REV, + INT64_ADD, + INT64_SUB, + INT64_MUL, + INT64_EXP, + INT64_SDIV, + INT64_SMOD, + INT64_UDIV, + INT64_UMOD, + INT64_AND, + INT64_XOR, + INT64_OR, + INT64_LSL, + INT64_LSR, + INT64_ASR, + INT64_SLT, + INT64_SLE, + INT64_SGT, + INT64_SGE, + INT64_ULT, + INT64_ULE, + INT64_UGT, + INT64_UGE, + INT64_EQ, + INT64_NE, + FLOAT64_ADD, + FLOAT64_SUB, + FLOAT64_MUL, + FLOAT64_DIV, + FLOAT64_EXP, + FLOAT64_EQ, + INT8_LOAD, + INT16_LOAD, + INT32_LOAD, + INT64_LOAD, + FLOAT32_LOAD, + FLOAT64_LOAD, + INT8_STORE, + INT16_STORE, + INT32_STORE, + INT64_STORE, + FLOAT32_STORE, + FLOAT64_STORE, + INT32_TO_FLOAT64, + INT64_TO_FLOAT64, + FLOAT64_TO_INT64, + TAG64_TO_INT1, + }; + + OpCode() = default; + explicit constexpr OpCode(Op op) : op(op) {} + operator Op() const + { + return this->op; + } + explicit operator bool() const = delete; + [[nodiscard]] Properties GetProperties() const; + [[nodiscard]] std::array GetOpCodeNumInsArray(BitField bitfield) const; + [[nodiscard]] size_t GetOpCodeNumIns(BitField bitfield) const; + [[nodiscard]] ValueCode GetValueCode() const; + [[nodiscard]] ValueCode GetInValueCode(BitField bitfield, size_t idx) const; + [[nodiscard]] OpCode GetInStateCode(size_t idx) const; + [[nodiscard]] std::string Str() const; + [[nodiscard]] bool IsRoot() const; + [[nodiscard]] bool IsProlog() const; + [[nodiscard]] bool IsFixed() const; + [[nodiscard]] bool IsSchedulable() const; + [[nodiscard]] bool IsState() const; // note: IsState(STATE_ENTRY) == false + [[nodiscard]] bool IsGeneralState() const; + [[nodiscard]] bool IsTerminalState() const; + [[nodiscard]] bool IsCFGMerge() const; + ~OpCode() = default; + +private: + Op op; +}; + +struct Properties { + ValueCode returnValue; + std::optional, bool>> statesIn; + size_t dependsIn; + std::optional, bool>> valuesIn; + std::optional states; +}; + +enum MarkCode : GateMark { + EMPTY, + VISITED, + FINISHED, +}; + +ValueCode JSValueCode(); +ValueCode PtrValueCode(); +size_t GetValueBits(ValueCode valueCode); + +class Out { +public: + Out() = default; + void SetNextOut(const Out *ptr); + [[nodiscard]] Out *GetNextOut(); + [[nodiscard]] const Out *GetNextOutConst() const; + void SetPrevOut(const Out *ptr); + [[nodiscard]] Out *GetPrevOut(); + [[nodiscard]] const Out *GetPrevOutConst() const; + void SetIndex(OutIdx idx); + [[nodiscard]] OutIdx GetIndex() const; + [[nodiscard]] Gate *GetGate(); + [[nodiscard]] const Gate *GetGateConst() const; + void SetPrevOutNull(); + [[nodiscard]] bool IsPrevOutNull() const; + void SetNextOutNull(); + [[nodiscard]] bool IsNextOutNull() const; + ~Out() = default; + +private: + OutIdx idx; + AddrShift nextOut; + AddrShift prevOut; +}; + +class In { +public: + In() = default; + void SetGate(const Gate *ptr); + [[nodiscard]] Gate *GetGate(); + [[nodiscard]] const Gate *GetGateConst() const; + void SetGateNull(); + [[nodiscard]] bool IsGateNull() const; + ~In() = default; + +private: + AddrShift gatePtr; +}; + +class Gate { +public: + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + Gate(GateId id, OpCode opcode, BitField bitfield, Gate *inList[], TypeCode type, MarkCode mark); + [[nodiscard]] static size_t GetGateSize(size_t numIns); + [[nodiscard]] size_t GetGateSize() const; + [[nodiscard]] static size_t GetOutListSize(size_t numIns); + [[nodiscard]] size_t GetOutListSize() const; + [[nodiscard]] static size_t GetInListSize(size_t numIns); + [[nodiscard]] size_t GetInListSize() const; + void NewIn(size_t idx, Gate *in); + void ModifyIn(size_t idx, Gate *in); + void DeleteIn(size_t idx); + void DeleteGate(); + [[nodiscard]] Out *GetOut(size_t idx); + [[nodiscard]] Out *GetFirstOut(); + [[nodiscard]] const Out *GetFirstOutConst() const; + // note: GetFirstOut() is not equal to GetOut(0) + // note: behavior of GetFirstOut() is undefined when there are no Outs + // note: use IsFirstOutNull() to check first if there may be no Outs + void SetFirstOut(const Out *firstOut); + void SetFirstOutNull(); + [[nodiscard]] bool IsFirstOutNull() const; + [[nodiscard]] In *GetIn(size_t idx); + [[nodiscard]] const In *GetInConst(size_t idx) const; + [[nodiscard]] Gate *GetInGate(size_t idx); + [[nodiscard]] const Gate *GetInGateConst(size_t idx) const; + // note: behavior of GetInGate(idx) is undefined when Ins[idx] is deleted or not assigned + // note: use IsInGateNull(idx) to check first if Ins[idx] may be deleted or not assigned + [[nodiscard]] bool IsInGateNull(size_t idx) const; + [[nodiscard]] OpCode GetOpCode() const; + void SetOpCode(OpCode opcode); + [[nodiscard]] GateId GetId() const; + [[nodiscard]] size_t GetNumIns() const; + [[nodiscard]] std::array GetNumInsArray() const; + [[nodiscard]] BitField GetBitField() const; + void SetBitField(BitField bitfield); + void AppendIn(const Gate *in); // considered very slow + void Print(bool inListPreview = false, size_t highlightIdx = -1) const; + std::optional> CheckNullInput() const; + std::optional> CheckStateInput() const; + std::optional> CheckValueInput() const; + std::optional> CheckDependInput() const; + std::optional> CheckStateOutput() const; + std::optional> CheckBranchOutput() const; + std::optional> CheckNOP() const; + std::optional> CheckSelector() const; + std::optional> CheckRelay() const; + std::optional> SpecialCheck() const; + [[nodiscard]] bool Verify() const; + [[nodiscard]] MarkCode GetMark(TimeStamp stamp) const; + void SetMark(MarkCode mark, TimeStamp stamp); + ~Gate() = default; + +private: + // ... + // out(2) + // out(1) + // out(0) + GateId id; + OpCode opcode; + TypeCode type; + TimeStamp stamp; + MarkCode mark; + BitField bitfield; + AddrShift firstOut; + // in(0) + // in(1) + // in(2) + // ... +}; +} // namespace kungfu + +#endif // PANDA_RUNTIME_ECMASCRIPT_COMPILER_GATE_H diff --git a/ecmascript/compiler/llvm_ir_builder.cpp b/ecmascript/compiler/llvm_ir_builder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9092e87b0fefd328f5d59b32acf10c4ee9331a32 --- /dev/null +++ b/ecmascript/compiler/llvm_ir_builder.cpp @@ -0,0 +1,1063 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/compiler/llvm_ir_builder.h" + +#include + +#include "ecmascript/compiler/circuit.h" +#include "ecmascript/compiler/fastpath_optimizer.h" +#include "ecmascript/compiler/gate.h" +#include "ecmascript/compiler/stub_interface.h" +#include "ecmascript/js_array.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Support/Host.h" +#include "llvm_mcjit_compiler.h" +#include "securec.h" + +namespace kungfu { +std::unordered_map g_values = {}; + +LLVMIRBuilder::LLVMIRBuilder(const std::vector> *schedule, const Circuit *circuit) + : m_schedule(schedule), m_circuit(circuit) +{ + m_module = LLVMModuleCreateWithName("simple_module"); + LLVMSetTarget(m_module, "x86_64-unknown-linux-gnu"); + m_builder = LLVMCreateBuilder(); + m_context = LLVMGetGlobalContext(); + LLVMTypeRef paramTys[] = { + LLVMInt32Type(), + }; + m_function = LLVMAddFunction(m_module, "foo", LLVMFunctionType(LLVMInt32Type(), paramTys, 1, 0)); + m_bbIdMapBb.clear(); +} + +LLVMIRBuilder::LLVMIRBuilder(const std::vector> *schedule, const Circuit *circuit, + LLVMModuleRef module, LLVMValueRef function) + : m_schedule(schedule), m_circuit(circuit), m_module(module), m_function(function) +{ + LLVMSetTarget(m_module, "x86_64-unknown-linux-gnu"); + m_builder = LLVMCreateBuilder(); + m_context = LLVMGetGlobalContext(); + m_bbIdMapBb.clear(); +} + +int LLVMIRBuilder::FindBasicBlock(AddrShift gate) const +{ + for (size_t bbIdx = 0; bbIdx < m_schedule->size(); bbIdx++) { + for (size_t instIdx = (*m_schedule)[bbIdx].size(); instIdx > 0; instIdx--) { + AddrShift tmp = (*m_schedule)[bbIdx][instIdx - 1]; + if (tmp == gate) { + return bbIdx; + } + } + } + return -1; +} + +void LLVMIRBuilder::Build() +{ + std::cout << "LLVM IR Builder Create Id Map of Blocks..." << std::endl; + for (size_t bbIdx = 0; bbIdx < m_schedule->size(); bbIdx++) { + for (size_t instIdx = (*m_schedule)[bbIdx].size(); instIdx > 0; instIdx--) { + GateId gateId = m_circuit->GetId((*m_schedule)[bbIdx][instIdx - 1]); + m_instIdMapBbId[gateId] = bbIdx; + } + } + + std::cout << "LLVM IR Builder Visit Gate..." << std::endl; + for (size_t bbIdx = 0; bbIdx < (*m_schedule).size(); bbIdx++) { + OperandsVector predecessors; + for (auto in : m_circuit->GetInVector((*m_schedule)[bbIdx][0])) { + if (!m_circuit->GetOpCode(in).IsState()) { + continue; + } + predecessors.insert(m_instIdMapBbId[m_circuit->GetId(in)]); + } + VisitBlock(bbIdx, predecessors); + + for (size_t instIdx = (*m_schedule)[bbIdx].size(); instIdx > 0; instIdx--) { + AddrShift gate = (*m_schedule)[bbIdx][instIdx - 1]; + std::vector ins = m_circuit->GetInVector(gate); + std::vector outs = m_circuit->GetOutVector(gate); + switch (m_circuit->GetOpCode(gate)) { + case OpCode::NOP: + break; + case OpCode::CIRCUIT_ROOT: + break; + case OpCode::STATE_ENTRY: { + int block = m_instIdMapBbId[m_circuit->GetId(gate)]; + int bbOut = m_instIdMapBbId[m_circuit->GetId(outs[0])]; + VisitGoto(block, bbOut); + break; + } + case OpCode::DEPEND_ENTRY: + break; + case OpCode::FRAMESTATE_ENTRY: + break; + case OpCode::RETURN_LIST: + break; + case OpCode::THROW_LIST: + break; + case OpCode::CONSTANT_LIST: + break; + case OpCode::ARG_LIST: + break; + case OpCode::RETURN: { + // [STATE] [DEPEND] [VALUE] [RETURN_LIST] + VisitReturn(gate, 1, ins); + break; + } + case OpCode::THROW: + break; + case OpCode::IF_BRANCH: { + AddrShift bTrue = (m_circuit->GetOpCode(outs[0]) == OpCode::IF_TRUE) ? outs[0] : outs[1]; + AddrShift bFalse = (m_circuit->GetOpCode(outs[0]) == OpCode::IF_FALSE) ? outs[0] : outs[1]; + int bbTrue = m_instIdMapBbId[m_circuit->GetId(bTrue)]; + int bbFalse = m_instIdMapBbId[m_circuit->GetId(bFalse)]; + VisitBranch(gate, ins[1], bbTrue, bbFalse); + break; + } + case OpCode::SWITCH_BRANCH: { + VisitSwitch(gate, ins[1], outs); + break; + } + case OpCode::ORDINARY_BLOCK: + case OpCode::IF_TRUE: + case OpCode::IF_FALSE: + case OpCode::SWITCH_CASE: + case OpCode::DEFAULT_CASE: { + int block = m_instIdMapBbId[m_circuit->GetId(gate)]; + int bbOut = m_instIdMapBbId[m_circuit->GetId(outs[0])]; + VisitGoto(block, bbOut); + break; + } + case OpCode::MERGE: { + int block = m_instIdMapBbId[m_circuit->GetId(gate)]; + int bbOut; + for (int i = 0; i < static_cast(outs.size()); i++) { + bbOut = m_instIdMapBbId[m_circuit->GetId(outs[i])]; + VisitGoto(block, bbOut); + } + break; + } + case OpCode::LOOP_BEGIN: { + int block = m_instIdMapBbId[m_circuit->GetId(gate)]; + int bbOut; + for (int i = 0; i < static_cast(outs.size()); i++) { + bbOut = m_instIdMapBbId[m_circuit->GetId(outs[i])]; + VisitGoto(block, bbOut); + } + break; + } + case OpCode::LOOP_BACK: { + int block = m_instIdMapBbId[m_circuit->GetId(gate)]; + int bbOut = m_instIdMapBbId[m_circuit->GetId(outs[0])]; + VisitGoto(block, bbOut); + break; + } + case OpCode::VALUE_SELECTOR_INT1: { + VisitPhi(gate, ins, MachineRep::K_BIT); + break; + } + case OpCode::VALUE_SELECTOR_INT32: { + VisitPhi(gate, ins, MachineRep::K_WORD32); + break; + } + case OpCode::VALUE_SELECTOR_INT64: { + VisitPhi(gate, ins, MachineRep::K_WORD64); + break; + } + case OpCode::VALUE_SELECTOR_FLOAT64: { + VisitPhi(gate, ins, MachineRep::K_FLOAT64); + break; + } + case OpCode::DEPEND_SELECTOR: + break; + case OpCode::DEPEND_RELAY: + break; + case OpCode::CALL: + case OpCode::INT1_CALL: + case OpCode::INT32_CALL: + case OpCode::INT64_CALL: { + VisitCall(gate, ins); + break; + } + case OpCode::ALLOCA: { + VisitAlloca(gate); + break; + } + case OpCode::INT1_ARG: // no break, fall through + case OpCode::INT32_ARG: // no break, fall through + case OpCode::INT64_ARG: { + VisitParameter(gate); + break; + } + case OpCode::INT32_CONSTANT: { + int32_t value = m_circuit->GetBitField(gate); + VisitInt32Constant(gate, value); + break; + } + case OpCode::JS_CONSTANT: // no break, fall through + case OpCode::INT64_CONSTANT: { + int64_t value = m_circuit->GetBitField(gate); + VisitInt64Constant(gate, value); + break; + } + case OpCode::FLOAT64_CONSTANT: { + int64_t value = m_circuit->GetBitField(gate); + VisitFloat64Constant(gate, value); + break; + } + case OpCode::ZEXT_INT1_TO_INT32: { + VisitZExtInt(gate, ins[0], MachineRep::K_WORD32); + break; + } + case OpCode::ZEXT_INT32_TO_INT64: // no break, fall through + case OpCode::ZEXT_INT1_TO_INT64: { + VisitZExtInt(gate, ins[0], MachineRep::K_WORD64); + break; + } + case OpCode::SEXT_INT1_TO_INT32: { + VisitSExtInt(gate, ins[0], MachineRep::K_WORD32); + break; + } + case OpCode::SEXT_INT1_TO_INT64: // no break, fall through + case OpCode::SEXT_INT32_TO_INT64: { + VisitSExtInt(gate, ins[0], MachineRep::K_WORD64); + break; + } + case OpCode::TRUNC_INT64_TO_INT1: + case OpCode::TRUNC_INT32_TO_INT1: { + VisitCastIntXToIntY(gate, ins[0], MachineRep::K_BIT); + break; + } + case OpCode::TRUNC_INT64_TO_INT32: { + VisitCastIntXToIntY(gate, ins[0], MachineRep::K_WORD32); + break; + } + case OpCode::INT32_REV: // no break, fall through + case OpCode::INT64_REV: { + VisitIntRev(gate, ins[0]); + break; + } + case OpCode::INT32_ADD: { + VisitIntAdd(gate, ins[0], ins[1], MachineRep::K_WORD32); + break; + } + case OpCode::INT64_ADD: { + VisitIntAdd(gate, ins[0], ins[1], MachineRep::K_WORD64); + break; + } + case OpCode::FLOAT64_ADD: { + VisitFloatAdd(gate, ins[0], ins[1]); + break; + } + case OpCode::FLOAT64_SUB: { + VisitFloatSub(gate, ins[0], ins[1]); + break; + } + case OpCode::FLOAT64_MUL: { + VisitFloatMul(gate, ins[0], ins[1]); + break; + } + case OpCode::FLOAT64_DIV: { + VisitFloatDiv(gate, ins[0], ins[1]); + break; + } + case OpCode::INT32_SUB: // no break, fall through + case OpCode::INT64_SUB: { + VisitIntSub(gate, ins[0], ins[1]); + break; + } + case OpCode::INT32_MUL: // no break, fall through + case OpCode::INT64_MUL: { + VisitIntMul(gate, ins[0], ins[1]); + break; + } + case OpCode::INT32_AND: // no break, fall through + case OpCode::INT64_AND: { + VisitIntAnd(gate, ins[0], ins[1]); + break; + } + case OpCode::INT32_OR: // no break, fall through + case OpCode::INT64_OR: { + VisitIntOr(gate, ins[0], ins[1]); + break; + } + case OpCode::INT32_LSR: // no break, fall through + case OpCode::INT64_LSR: { + VisitIntLsr(gate, ins[0], ins[1]); + break; + } + case OpCode::INT32_SLT: // no break, fall through + case OpCode::INT64_SLT: { + VisitIntOrUintCmp(gate, ins[0], ins[1], LLVMIntSLT); + break; + } + case OpCode::INT32_SLE: // no break, fall through + case OpCode::INT64_SLE: { + VisitIntOrUintCmp(gate, ins[0], ins[1], LLVMIntSLE); + break; + } + case OpCode::INT32_SGT: // no break, fall through + case OpCode::INT64_SGT: { + VisitIntOrUintCmp(gate, ins[0], ins[1], LLVMIntSGT); + break; + } + case OpCode::INT32_SGE: // no break, fall through + case OpCode::INT64_SGE: { + VisitIntOrUintCmp(gate, ins[0], ins[1], LLVMIntSGE); + break; + } + case OpCode::INT32_EQ: // no break, fall through + case OpCode::INT64_EQ: { + VisitIntOrUintCmp(gate, ins[0], ins[1], LLVMIntEQ); + break; + } + case OpCode::FLOAT64_EQ: { + VisitFloatOrDoubleCmp(gate, ins[0], ins[1], LLVMRealUEQ); + break; + } + case OpCode::INT32_NE: // no break, fall through + case OpCode::INT64_NE: { + VisitIntOrUintCmp(gate, ins[0], ins[1], LLVMIntNE); + break; + } + case OpCode::INT32_LOAD: { + AddrShift base = ins[1]; + VisitLoad(gate, MachineRep::K_WORD32, base); + break; + } + case OpCode::INT64_LOAD: { + AddrShift base = ins[1]; + VisitLoad(gate, MachineRep::K_WORD64, base); + break; + } + case OpCode::INT32_STORE: { + VisitStore(gate, MachineRep::K_WORD32, ins[2], ins[1]); // 2:baseAddr gate, 1:data gate + break; + } + case OpCode::INT64_STORE: { + VisitStore(gate, MachineRep::K_WORD64, ins[2], ins[1]); // 2:baseAddr gate, 1:data gate + break; + } + case OpCode::INT32_TO_FLOAT64: // no break, fall through + case OpCode::INT64_TO_FLOAT64: { + VisitCastIntToDouble(gate, ins[0]); + break; + } + case OpCode::FLOAT64_TO_INT64: { + VisitCastDoubleToInt(gate, ins[0]); + break; + } + default: { + std::cout << "The gate below need to be translated " << std::endl; + m_circuit->Print(gate); + UNREACHABLE(); + } + } + } + } + End(); +} + +BasicBlock *LLVMIRBuilder::EnsurBasicBlock(int id) +{ + BasicBlock *bb = nullptr; + if (m_bbIdMapBb.count(id) == 0) { + auto newBB = std::make_unique(id); + bb = newBB.get(); + m_bbIdMapBb[id] = std::move(newBB); + } else { + bb = m_bbIdMapBb[id].get(); + } + return bb; +} + +void LLVMIRBuilder::StartLLVMBuilder(BasicBlock *bb) const +{ + EnsureLLVMBB(bb); + LLVMTFBuilderBasicBlockImpl *impl = bb->GetImpl(); + if ((impl == nullptr) || (impl->llvm_bb_ == nullptr)) { + std::cerr << "StartLLVMBuilder failed " << std::endl; + return; + } + impl->started = true; + bb->SetImpl(impl); + std::cout << "Basicblock id :" << bb->GetId() << + "impl:" << bb->GetImpl() << std::endl; + LLVMPositionBuilderAtEnd(m_builder, impl->llvm_bb_); +} + +void LLVMIRBuilder::ProcessPhiWorkList() +{ + for (BasicBlock *bb : m_phiRebuildWorklist) { + auto impl = bb->GetImpl(); + for (auto &e : impl->not_merged_phis) { + BasicBlock *pred = e.pred; + if (impl->started == 0) { + std::cerr << " ProcessPhiWorkList error hav't start " << std::endl; + return; + } + LLVMValueRef value = g_values[e.operand]; + if (LLVMTypeOf(value) != LLVMTypeOf(e.phi)) { + std::cerr << " ProcessPhiWorkList LLVMTypeOf don't match error " << std::endl; + } + LLVMBasicBlockRef llvmBB = EnsureLLVMBB(pred); + LLVMAddIncoming(e.phi, &value, &llvmBB, 1); + } + impl->not_merged_phis.clear(); + } + m_phiRebuildWorklist.clear(); +} + +void LLVMIRBuilder::EndCurrentBlock() const +{ + LLVMTFBuilderBasicBlockImpl *impl = m_currentBb->GetImpl(); + impl->ended = true; +} + +void LLVMIRBuilder::End() +{ + ASSERT(!!m_currentBb); + EndCurrentBlock(); + ProcessPhiWorkList(); + for (auto &it: m_bbIdMapBb) { + it.second->ResetImpl(); + } +} + +LLVMTFBuilderBasicBlockImpl *LLVMIRBuilder::EnsureLLVMBBImpl(BasicBlock *bb) const +{ + if (bb->GetImpl()) { + return bb->GetImpl(); + } + auto impl = std::make_unique(); + bb->SetImpl(impl.release()); + return bb->GetImpl(); +} + +LLVMBasicBlockRef LLVMIRBuilder::EnsureLLVMBB(BasicBlock *bb) const +{ + LLVMTFBuilderBasicBlockImpl *impl = EnsureLLVMBBImpl(bb); + if (impl->llvm_bb_) { + return impl->llvm_bb_; + } + std::string buf = "B" + std::to_string(bb->GetId()); + LLVMBasicBlockRef llvmBB = LLVMAppendBasicBlock(m_function, buf.c_str()); + impl->llvm_bb_ = llvmBB; + impl->continuation = llvmBB; + bb->SetImpl(impl); + std::cout << std::endl + << "create LLVMBB = " << buf << " impl:" << bb->GetImpl() << std::endl; + return llvmBB; +} + +LLVMTypeRef LLVMIRBuilder::GetMachineRepType(MachineRep rep) const +{ + LLVMTypeRef dstType; + switch (rep) { + case MachineRep::K_BIT: + dstType = LLVMInt1TypeInContext(m_context); + break; + case MachineRep::K_WORD8: + dstType = LLVMInt8TypeInContext(m_context); + break; + case MachineRep::K_WORD32: + dstType = LLVMInt32TypeInContext(m_context); + break; + case MachineRep::K_FLOAT64: + dstType = LLVMDoubleTypeInContext(m_context); + break; + default: // 64bit int goes to default scenario + dstType = LLVMInt64TypeInContext(m_context); + break; + } + return dstType; +} + +void LLVMIRBuilder::VisitCall(AddrShift gate, const std::vector &inList) +{ + constexpr int paraStartIndex = 2; + int index = m_circuit->GetBitField(inList[1]); + LLVMValueRef callee = reinterpret_cast(FastStubs::GetInstance().GetFastStub(index)); + LLVMValueRef params[16]; // 16: number of param + for (size_t paraIdx = paraStartIndex; paraIdx < inList.size(); ++paraIdx) { + AddrShift gateTmp = inList[paraIdx]; + params[paraIdx - paraStartIndex] = g_values[gateTmp]; + m_circuit->Print(gateTmp); + std::cout << "arg" << paraIdx - paraStartIndex << " th " << std::endl; + LLVMDumpValue(params[paraIdx - paraStartIndex]); + std::cout << std::endl; + } + if (callee == nullptr) { + std::cout << "callee nullptr" << std::endl; + return; + } + g_values[gate] = LLVMBuildCall(m_builder, callee, params, inList.size() - paraStartIndex, ""); + return; +} + +void LLVMIRBuilder::VisitAlloca(AddrShift gate) +{ + std::cout << " VisitAlloca " << std::endl; + uint64_t sizeEnum = m_circuit->GetBitField(gate); + LLVMTypeRef sizeType = GetMachineRepType(static_cast(sizeEnum)); + std::cout << LLVMGetTypeKind(sizeType) << std::endl; + g_values[gate] = LLVMBuildPtrToInt(m_builder, LLVMBuildAlloca(m_builder, sizeType, ""), + LLVMInt64Type(), ""); // NOTE: pointer val is currently viewed as 64bits + return; +} + +void LLVMIRBuilder::VisitPhi(AddrShift gate, const std::vector &srcGates, MachineRep rep) +{ + std::cout << "--------------- VisitPhi ------------------------" << std::endl; + LLVMTypeRef type = GetMachineRepType(rep); + LLVMValueRef phi = LLVMBuildPhi(m_builder, type, ""); + std::vector relMergeIns = m_circuit->GetInVector(srcGates[0]); + bool addToPhiRebuildList = false; + for (int i = 1; i < static_cast(srcGates.size()); i++) { + GateId gateId = m_circuit->GetId(relMergeIns[i - 1]); + int bbIdx = m_instIdMapBbId[gateId]; + std::cout << "srcGate: " << srcGates[i] << " dominated gateId:" << gateId << "dominated bbIdx: " << bbIdx + << std::endl; + int cnt = m_bbIdMapBb.count(bbIdx); + // if cnt = 0 means bb with current bbIdx hasn't been created + if (cnt > 0) { + BasicBlock *bb = m_bbIdMapBb[bbIdx].get(); + std::cout << "bb : " << bb << std::endl; + if (bb == nullptr) { + std::cerr << "VisitPhi failed BasicBlock nullptr" << std::endl; + return; + } + LLVMTFBuilderBasicBlockImpl *impl = bb->GetImpl(); + if (impl == nullptr) { + std::cerr << "VisitPhi failed impl nullptr" << std::endl; + return; + } + LLVMBasicBlockRef llvmBB = EnsureLLVMBB(bb); // The llvm bb + LLVMValueRef value = g_values[srcGates[i]]; + if (impl->started) { + LLVMAddIncoming(phi, &value, &llvmBB, 1); + } else { + addToPhiRebuildList = true; + impl = m_currentBb->GetImpl(); + impl->not_merged_phis.emplace_back(); + auto ¬_merged_phi = impl->not_merged_phis.back(); + not_merged_phi.phi = phi; + not_merged_phi.pred = bb; + not_merged_phi.operand = srcGates[i]; + } + } else { + addToPhiRebuildList = true; + } + if (addToPhiRebuildList == true) { + m_phiRebuildWorklist.push_back(m_currentBb); + } + g_values[gate] = phi; + } + std::cout << "+++++++++++++++ VisitPhi ++++++++++++++++++++++++" << std::endl; +} + +void LLVMIRBuilder::VisitReturn(AddrShift gate, AddrShift popCount, const std::vector &operands) const +{ + // [STATE] [DEPEND] [VALUE] [RETURN_LIST] + std::cout << "VisitReturn -" << std::endl; + AddrShift operand = operands[2]; // 2: skip 2 in gate that are not data gate + std::cout << "VisitReturn gate: " << gate << " popCount: " << popCount << std::endl; + std::cout << "VisitReturn return: " << operand << " gateId: " << m_circuit->GetId(operand) << std::endl; + LLVMValueRef returnValue = g_values[operand]; + LLVMDumpValue(returnValue); + std::cout << std::endl; + LLVMBuildRet(m_builder, returnValue); + std::cout << "VisitReturn +" << std::endl; +} + +void LLVMIRBuilder::VisitBlock(int gate, const OperandsVector &predecessors) // NOLINTNEXTLINE(misc-unused-parameters) +{ + std::cout << "VisitBlock BBIdx:" << gate << " -" << std::endl; + BasicBlock *bb = EnsurBasicBlock(gate); + if (bb == nullptr) { + std::cerr << "StartLLVMBuilder failed " << std::endl; + return; + } + m_currentBb = bb; + LLVMBasicBlockRef llvmbb = EnsureLLVMBB(bb); + StartLLVMBuilder(bb); + std::cout << "predecessors :"; + for (int predecessor : predecessors) { + BasicBlock *pre = EnsurBasicBlock(predecessor); + if (pre == nullptr) { + std::cerr << "StartLLVMBuilder failed predecessor:%d nullptr" << predecessor << std::endl; + return; + } + LLVMBasicBlockRef llvmpre = EnsureLLVMBB(pre); + std::cout << " " << predecessor; + LLVMMoveBasicBlockBefore(llvmpre, llvmbb); + } + std::cout << "VisitBlock BBIdx:" << gate << " +" << std::endl; +} + +void LLVMIRBuilder::VisitGoto(int block, int bbOut) +{ + if (block == bbOut) { + return; + } + BasicBlock *bb = EnsurBasicBlock(bbOut); + if (bb == nullptr) { + std::cerr << "StartLLVMBuilder failed " << std::endl; + return; + } + llvm::BasicBlock *self = llvm::unwrap(EnsureLLVMBB(m_bbIdMapBb[block].get())); + llvm::BasicBlock *out = llvm::unwrap(EnsureLLVMBB(m_bbIdMapBb[bbOut].get())); + llvm::BranchInst::Create(out, self); + EndCurrentBlock(); +} + +void LLVMIRBuilder::VisitInt32Constant(AddrShift gate, int32_t value) const +{ + LLVMValueRef llvmValue = LLVMConstInt(LLVMInt32Type(), value, 0); + LLVMTFBuilderBasicBlockImpl *impl = m_currentBb->GetImpl(); + impl->values_[gate] = llvmValue; + g_values[gate] = llvmValue; + char *str = LLVMPrintValueToString(llvmValue); + std::cout << "VisitInt32Constant set gate:" << gate << " value:" << value << " impl:" << impl << std::endl; + std::cout << "VisitInt32Constant " << str << std::endl; +} + +void LLVMIRBuilder::VisitInt64Constant(AddrShift gate, int64_t value) const +{ + LLVMValueRef llvmValue = LLVMConstInt(LLVMInt64Type(), value, 0); + LLVMTFBuilderBasicBlockImpl *impl = m_currentBb->GetImpl(); + impl->values_[gate] = llvmValue; + g_values[gate] = llvmValue; + char *str = LLVMPrintValueToString(llvmValue); + std::cout << "VisitInt64Constant set gate:" << gate << " value:" << value << " impl:" << impl << std::endl; + std::cout << "VisitInt64Constant " << str << std::endl; +} + +void LLVMIRBuilder::VisitFloat64Constant(AddrShift gate, int64_t value) const +{ + LLVMValueRef llvmValue = LLVMConstReal(LLVMDoubleType(), value); + LLVMTFBuilderBasicBlockImpl *impl = m_currentBb->GetImpl(); + impl->values_[gate] = llvmValue; + g_values[gate] = llvmValue; + char *str = LLVMPrintValueToString(llvmValue); + std::cout << "VisitFloat64Constant set gate:" << gate << " value:" << value << " impl:" << impl << std::endl; + std::cout << "VisitFloat64Constant " << str << std::endl; +} + +void LLVMIRBuilder::VisitParameter(AddrShift gate) const +{ + int argth = m_circuit->LoadGatePtrConst(gate)->GetBitField(); + std::cout << "VisitParameter " << argth << "th parameter -" << std::endl; + LLVMValueRef value = LLVMGetParam(m_function, argth); + LLVMDumpValue(value); + LLVMTFBuilderBasicBlockImpl *impl = m_currentBb->GetImpl(); + impl->values_[gate] = value; + g_values[gate] = value; + std::cout << "VisitParameter set gate:" << gate << " value:" << value << " impl:" << impl << std::endl; + // NOTE: caller put args, otherwise crash + if (value == nullptr) { + std::cerr << "VisitParameter arg" << argth << "th nullptr" << std::endl; + return; + } + LLVMDumpValue(value); + char *str = LLVMPrintValueToString(value); + if (str != nullptr) { + std::cout << "VisitParameter arg" << argth << "th value = " << str << std::endl; + } + std::cout << "VisitParameter " << argth << "th parameter +" << std::endl; +} + +void LLVMIRBuilder::VisitBranch(AddrShift gate, AddrShift cmp, int btrue, int bfalse) +{ + std::cout << "VisitBranch - cmp:" << cmp << std::endl; + LLVMTFBuilderBasicBlockImpl *impl = EnsureLLVMBBImpl(m_currentBb); + if ((impl->values_.count(cmp) == 0) && (g_values.count(cmp) == 0)) { + std::cerr << "VisitBranch cmp empty !" << std::endl; + return; + } + LLVMValueRef cond = g_values[cmp]; + + BasicBlock *trueBB = EnsurBasicBlock(btrue); + BasicBlock *falseBB = EnsurBasicBlock(bfalse); + EnsureLLVMBB(trueBB); + EnsureLLVMBB(falseBB); + + LLVMBasicBlockRef llvmTrueBB = trueBB->GetImpl()->llvm_bb_; + LLVMBasicBlockRef llvmFalseBB = falseBB->GetImpl()->llvm_bb_; + LLVMValueRef result = LLVMBuildCondBr(m_builder, cond, llvmTrueBB, llvmFalseBB); + EndCurrentBlock(); + g_values[gate] = result; + std::cout << "VisitBranch + " << std::endl; +} + +void LLVMIRBuilder::VisitSwitch(AddrShift gate, AddrShift input, const std::vector &outList) +{ + std::cout << "VisitSwitch - " << std::endl; + LLVMValueRef cond = g_values[input]; + unsigned caseNum = outList.size(); + BasicBlock *curOutBB = nullptr; + LLVMBasicBlockRef llvmDefaultOutBB = nullptr; + for (int i = 0; i < static_cast(caseNum); i++) { + curOutBB = EnsurBasicBlock(m_instIdMapBbId[m_circuit->GetId(outList[i])]); + EnsureLLVMBB(curOutBB); + if (m_circuit->GetOpCode(outList[i]) == OpCode::DEFAULT_CASE) { + llvmDefaultOutBB = curOutBB->GetImpl()->llvm_bb_; + } + } + LLVMValueRef result = LLVMBuildSwitch(m_builder, cond, llvmDefaultOutBB, caseNum - 1); + LLVMBasicBlockRef llvmCurOutBB = nullptr; + for (int i = 0; i < static_cast(caseNum - 1); i++) { + if (m_circuit->GetOpCode(outList[i]) == OpCode::DEFAULT_CASE) { + continue; + } + curOutBB = EnsurBasicBlock(m_instIdMapBbId[m_circuit->GetId(outList[i])]); + llvmCurOutBB = curOutBB->GetImpl()->llvm_bb_; + LLVMAddCase(result, LLVMConstInt(LLVMInt64Type(), m_circuit->GetBitField(outList[i]), 0), llvmCurOutBB); + } + EndCurrentBlock(); + g_values[gate] = result; + std::cout << "VisitSwitch + " << std::endl; +} + +void LLVMIRBuilder::VisitLoad(AddrShift gate, MachineRep rep, AddrShift base) const +{ + std::cout << "VisitLoad base:" << base << std::endl; + + LLVMValueRef baseAddr = g_values[base]; + if (LLVMGetTypeKind(LLVMTypeOf(baseAddr)) == LLVMIntegerTypeKind) { + baseAddr = LLVMBuildIntToPtr(m_builder, baseAddr, LLVMPointerType(GetMachineRepType(rep), 0), ""); + } + baseAddr = LLVMBuildPointerCast(m_builder, baseAddr, LLVMPointerType(GetMachineRepType(rep), 0), ""); + LLVMValueRef value = LLVMBuildLoad(m_builder, baseAddr, ""); + g_values[gate] = value; + std::cout << "VisitLoad value:" << value << " " + << "value type" << LLVMTypeOf(value) << std::endl; +} + +void LLVMIRBuilder::VisitStore(AddrShift gate, MachineRep rep, AddrShift base, AddrShift dataToStore) const +{ + std::cout << "VisitStore base:" << base << std::endl; + LLVMValueRef baseAddr = g_values[base]; + if (LLVMGetTypeKind(LLVMTypeOf(baseAddr)) == LLVMIntegerTypeKind) { + baseAddr = LLVMBuildIntToPtr(m_builder, baseAddr, LLVMPointerType(GetMachineRepType(rep), 0), ""); + } + baseAddr = LLVMBuildPointerCast(m_builder, baseAddr, LLVMPointerType(GetMachineRepType(rep), 0), ""); + LLVMValueRef value = LLVMBuildStore(m_builder, g_values[dataToStore], baseAddr); + g_values[gate] = value; + std::cout << "VisitStore value:" << value << " " + << "value type" << LLVMTypeOf(value) << std::endl; +} +void LLVMIRBuilder::VisitIntOrUintCmp(AddrShift gate, AddrShift e1, AddrShift e2, LLVMIntPredicate opcode) const +{ + std::cout << "VisitIntOrUintCmp -" << std::endl; + LLVMTFBuilderBasicBlockImpl *impl = m_currentBb->GetImpl(); + std::cout << "VisitIntOrUintCmp get gate:" << e1 << " value:" << g_values[e1] << " impl:" << impl << std::endl; + + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << " LLVMDumpValue e2 " << std::endl; + + LLVMValueRef e2Value = g_values[e2]; + LLVMDumpValue(e2Value); + LLVMValueRef result = LLVMBuildICmp(m_builder, opcode, e1Value, e2Value, ""); + + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitIntOrUintCmp set gate:" << gate << " value:" << result << " impl:" << impl << std::endl; + std::cout << "VisitIntOrUintCmp +" << std::endl; +} + +void LLVMIRBuilder::VisitFloatOrDoubleCmp(AddrShift gate, AddrShift e1, AddrShift e2, LLVMRealPredicate opcode) const +{ + std::cout << "VisitFloatOrDoubleCmp -" << std::endl; + LLVMTFBuilderBasicBlockImpl *impl = m_currentBb->GetImpl(); + std::cout << "VisitFloatOrDouble get gate:" << e1 << " value:" << g_values[e1] << " impl:" << impl << std::endl; + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << " LLVMDumpValue e2 " << std::endl; + LLVMValueRef e2Value = g_values[e2]; + LLVMDumpValue(e2Value); + LLVMValueRef result = LLVMBuildFCmp(m_builder, opcode, e1Value, e2Value, ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitFloatOrDoubleCmp set gate:" << gate << " value:" << result << " impl:" << impl << std::endl; + std::cout << "VisitFloatOrDoubleCmp +" << std::endl; +} + +void LLVMIRBuilder::VisitIntRev(AddrShift gate, AddrShift e1) const +{ + std::cout << "VisitIntRev -" << std::endl; + std::cout << "VisitIntRev get gate:" << e1 << " value:" << std::endl; + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << std::endl; + LLVMValueRef result = LLVMBuildNeg(m_builder, e1Value, ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << std::endl; + std::cout << "VisitIntRev set gate:" << gate << " value:" << result << std::endl; + std::cout << "VisitIntRev +" << std::endl; +} + +void LLVMIRBuilder::VisitIntAdd(AddrShift gate, AddrShift e1, AddrShift e2, MachineRep rep) const +{ + std::cout << "VisitIntAdd -" << std::endl; + std::cout << "VisitIntAdd get gate:" << e1 << std::endl; + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << " LLVMDumpValue e2 " << std::endl; + LLVMValueRef e2Value = g_values[e2]; + LLVMDumpValue(e2Value); + if (LLVMGetTypeKind(LLVMTypeOf(e1Value)) == LLVMPointerTypeKind) { // for scenario: pointer + offset + e1Value = LLVMBuildPtrToInt(m_builder, e1Value, GetMachineRepType(rep), ""); + } + LLVMValueRef result = LLVMBuildAdd(m_builder, e1Value, e2Value, ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitIntAdd set gate:" << gate << " value:" << result << std::endl; + std::cout << "VisitIntAdd +" << std::endl; +} + +void LLVMIRBuilder::VisitFloatAdd(AddrShift gate, AddrShift e1, AddrShift e2) const +{ + std::cout << "VisitFloatAdd -" << std::endl; + std::cout << "VisitFloatAdd get gate:" << e1 << std::endl; + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << " LLVMDumpValue e2 " << std::endl; + LLVMValueRef e2Value = g_values[e2]; + LLVMDumpValue(e2Value); + LLVMValueRef result = LLVMBuildFAdd(m_builder, e1Value, e2Value, ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitFloatAdd set gate:" << gate << " value:" << result << std::endl; + std::cout << "VisitFloatAdd +" << std::endl; +} + +void LLVMIRBuilder::VisitFloatSub(AddrShift gate, AddrShift e1, AddrShift e2) const +{ + std::cout << "VisitFloatSub -" << std::endl; + std::cout << "VisitFloatSub get gate:" << gate << std::endl; + std::cout << " LLVMDumpValue e1 " << std::endl; + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << " LLVMDumpValue e2 " << std::endl; + LLVMValueRef e2Value = g_values[e2]; + LLVMDumpValue(e2Value); + LLVMValueRef result = LLVMBuildFSub(m_builder, e1Value, e2Value, ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitFloatSub set gate:" << gate << " value:" << result << std::endl; + std::cout << "VisitFloatSub +" << std::endl; +} + +void LLVMIRBuilder::VisitFloatMul(AddrShift gate, AddrShift e1, AddrShift e2) const +{ + std::cout << "VisitFloatMul -" << std::endl; + std::cout << "VisitFloatMul get gate:" << gate << std::endl; + std::cout << " LLVMDumpValue e1 " << std::endl; + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << " LLVMDumpValue e2 " << std::endl; + LLVMValueRef e2Value = g_values[e2]; + LLVMDumpValue(e2Value); + LLVMValueRef result = LLVMBuildFMul(m_builder, e1Value, e2Value, ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitFloatMul set gate:" << gate << " value:" << result << std::endl; + std::cout << "VisitFloatMul +" << std::endl; +} + +void LLVMIRBuilder::VisitFloatDiv(AddrShift gate, AddrShift e1, AddrShift e2) const +{ + std::cout << "VisitFloatDiv -" << std::endl; + std::cout << "VisitFloatDiv get gate:" << gate << std::endl; + std::cout << " LLVMDumpValue e1 " << std::endl; + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << " LLVMDumpValue e2 " << std::endl; + LLVMValueRef e2Value = g_values[e2]; + LLVMDumpValue(e2Value); + LLVMValueRef result = LLVMBuildFDiv(m_builder, e1Value, e2Value, ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitFloatDiv set gate:" << gate << " value:" << result << std::endl; + std::cout << "VisitFloatDiv +" << std::endl; +} + +void LLVMIRBuilder::VisitIntSub(AddrShift gate, AddrShift e1, AddrShift e2) const +{ + std::cout << "VisitIntSub -" << std::endl; + std::cout << "VisitIntSub get gate:" << e1 << std::endl; + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << " LLVMDumpValue e2 " << std::endl; + LLVMValueRef e2Value = g_values[e2]; + LLVMDumpValue(e2Value); + LLVMValueRef result = LLVMBuildSub(m_builder, e1Value, e2Value, ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitIntSub set gate:" << gate << " value:" << result << std::endl; + std::cout << "VisitIntSub +" << std::endl; +} + +void LLVMIRBuilder::VisitIntMul(AddrShift gate, AddrShift e1, AddrShift e2) const +{ + std::cout << "VisitIntMul -" << std::endl; + LLVMTFBuilderBasicBlockImpl *impl = m_currentBb->GetImpl(); + std::cout << "VisitIntMul get gate:" << e1 << " value:" << (impl->values_[e1]) << " impl:" << impl << std::endl; + + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << " LLVMDumpValue e2 " << std::endl; + + LLVMValueRef e2Value = g_values[e2]; + LLVMDumpValue(e2Value); + LLVMValueRef result = LLVMBuildMul(m_builder, e1Value, e2Value, ""); + + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitIntMul set gate:" << gate << " value:" << result << " impl:" << impl << std::endl; + std::cout << "VisitIntMul +" << std::endl; +} + +void LLVMIRBuilder::VisitIntOr(AddrShift gate, AddrShift e1, AddrShift e2) const +{ + std::cout << "VisitIntOr -" << std::endl; + std::cout << "VisitIntOr get gate:" << e1 << std::endl; + std::cout << " LLVMDumpValue e1 " << std::endl; + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << " LLVMDumpValue e2 " << std::endl; + LLVMValueRef e2Value = g_values[e2]; + LLVMDumpValue(e2Value); + LLVMValueRef result = LLVMBuildOr(m_builder, e1Value, e2Value, ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitIntOr set gate:" << gate << " value:" << result << std::endl; + std::cout << "VisitIntOr +" << std::endl; +} + +void LLVMIRBuilder::VisitIntAnd(AddrShift gate, AddrShift e1, AddrShift e2) const +{ + std::cout << "VisitIntAnd -" << std::endl; + LLVMTFBuilderBasicBlockImpl *impl = m_currentBb->GetImpl(); + std::cout << "VisitIntAnd get gate:" << e1 << " value:" << (impl->values_[e1]) << " impl:" << impl << std::endl; + + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << " LLVMDumpValue e2 " << std::endl; + + LLVMValueRef e2Value = g_values[e2]; + LLVMDumpValue(e2Value); + LLVMValueRef result = LLVMBuildAnd(m_builder, e1Value, e2Value, ""); + + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitIntAnd set gate:" << gate << " value:" << result << " impl:" << impl << std::endl; + std::cout << "VisitIntAnd +" << std::endl; +} + +void LLVMIRBuilder::VisitIntLsr(AddrShift gate, AddrShift e1, AddrShift e2) const +{ + std::cout << "VisitIntLsr -" << std::endl; + std::cout << "VisitIntLsr get gate:" << e1 << std::endl; + std::cout << " LLVMDumpValue e1 " << std::endl; + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + std::cout << " LLVMDumpValue e2 " << std::endl; + LLVMValueRef e2Value = g_values[e2]; + LLVMDumpValue(e2Value); + LLVMValueRef result = LLVMBuildLShr(m_builder, e1Value, e2Value, ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitIntLsr set gate:" << gate << " value:" << result << std::endl; + std::cout << "VisitIntLsr +" << std::endl; +} +void LLVMIRBuilder::VisitZExtInt(AddrShift gate, AddrShift e1, MachineRep rep) const +{ + std::cout << "VisitZExtInt -" << std::endl; + LLVMTFBuilderBasicBlockImpl *impl = m_currentBb->GetImpl(); + std::cout << "VisitZExtInt get gate:" << e1 << "value:" << (impl->values_[e1]) << std::endl; + + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + LLVMValueRef result = LLVMBuildZExt(m_builder, e1Value, GetMachineRepType(rep), ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitZExtInt set gate:" << gate << " value:" << result << " impl:" << impl << std::endl; + std::cout << "VisitZExtInt +" << std::endl; +} + +void LLVMIRBuilder::VisitSExtInt(AddrShift gate, AddrShift e1, MachineRep rep) const +{ + std::cout << "VisitSExtInt -" << std::endl; + LLVMTFBuilderBasicBlockImpl *impl = m_currentBb->GetImpl(); + std::cout << "VisitSExtInt get gate:" << e1 << "value:" << (impl->values_[e1]) << std::endl; + + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + LLVMValueRef result = LLVMBuildSExt(m_builder, e1Value, GetMachineRepType(rep), ""); + + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitSExtInt set gate:" << gate << " value:" << result << " impl:" << impl << std::endl; + std::cout << "VisitSExtInt +" << std::endl; +} + +void LLVMIRBuilder::VisitCastIntXToIntY(AddrShift gate, AddrShift e1, MachineRep rep) const +{ + std::cout << "VisitCastIntXToIntY -" << std::endl; + LLVMTFBuilderBasicBlockImpl *impl = m_currentBb->GetImpl(); + std::cout << "VisitCastIntXToIntY get gate:" << e1 << "value:" << (impl->values_[e1]) << std::endl; + + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + LLVMValueRef result = LLVMBuildIntCast2(m_builder, e1Value, GetMachineRepType(rep), 1, ""); + + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitCastIntXToIntY set gate:" << gate << " value:" << result << " impl:" << impl << std::endl; + std::cout << "VisitCastIntXToIntY +" << std::endl; +} + +void LLVMIRBuilder::VisitCastIntToDouble(AddrShift gate, AddrShift e1) const +{ + std::cout << "VisitCastIntToDouble -" << std::endl; + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + LLVMValueRef result = LLVMBuildSIToFP(m_builder, e1Value, LLVMDoubleType(), ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitCastIntToDouble set gate:" << gate << " value:" << result << std::endl; + std::cout << "VisitCastIntToDouble +" << std::endl; +} + +void LLVMIRBuilder::VisitCastDoubleToInt(AddrShift gate, AddrShift e1) const +{ + std::cout << "VisitCastDoubleToInt -" << std::endl; + LLVMValueRef e1Value = g_values[e1]; + LLVMDumpValue(e1Value); + LLVMValueRef result = LLVMBuildFPToSI(m_builder, e1Value, LLVMInt64Type(), ""); + g_values[gate] = result; + LLVMDumpValue(result); + std::cout << "VisitCastDoubleToInt set gate:" << gate << " value:" << result << std::endl; + std::cout << "VisitCastDoubleToInt +" << std::endl; +} +} // namespace kungfu diff --git a/ecmascript/compiler/llvm_ir_builder.h b/ecmascript/compiler/llvm_ir_builder.h new file mode 100644 index 0000000000000000000000000000000000000000..34ba3bdc963bc612b08fb95d0c997dcf8a7d99e9 --- /dev/null +++ b/ecmascript/compiler/llvm_ir_builder.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_LLVM_IR_BUILDER_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_LLVM_IR_BUILDER_H + +#include +#include +#include +#include + +#include "ecmascript/compiler/circuit.h" +#include "ecmascript/compiler/gate.h" +#include "llvm-c/Core.h" +#include "llvm-c/Types.h" + +namespace kungfu { +using OperandsVector = std::set; +class BasicBlock; +using BasicBlockMap = std::map>; + +enum class MachineRep { + K_NONE, + K_BIT, + K_WORD8, + K_WORD16, + K_WORD32, + K_WORD64, + // FP representations must be last, and in order of increasing size. + K_FLOAT32, + K_FLOAT64, + K_SIMD128 +}; + +class BasicBlock { +public: + explicit BasicBlock(int id) : id_(id) + { + predecessors_ = {}; + successors_ = {}; + impl_ = nullptr; + } + + int GetId() const + { + return id_; + } + + template + inline T *GetImpl() const + { + return static_cast(impl_); + } + + inline void SetImpl(void *impl) + { + impl_ = impl; + } + + template + inline void ResetImpl() + { + if (impl_) { + delete GetImpl(); + impl_ = nullptr; + } + } + ~BasicBlock() = default; +private: + std::vector predecessors_{}; + std::vector successors_{}; + int id_{-1}; + void *impl_{nullptr}; +}; + +struct NotMergedPhiDesc { + BasicBlock *pred; + AddrShift operand; + LLVMValueRef phi; +}; + +struct LLVMTFBuilderBasicBlockImpl { + LLVMBasicBlockRef llvm_bb_ = nullptr; + LLVMBasicBlockRef continuation = nullptr; + LLVMBuilderRef llvm_builder_ = nullptr; + std::unordered_map values_ = {}; + bool started = false; + bool ended = false; + std::vector not_merged_phis; +}; + +class LLVMIRBuilder { +public: + LLVMIRBuilder(const std::vector> *schedule, const Circuit *circuit); + LLVMIRBuilder(const std::vector> *schedule, const Circuit *circuit, LLVMModuleRef module, + LLVMValueRef function); + + void Build(); + ~LLVMIRBuilder() = default; +private: + void VisitCall(AddrShift gate, const std::vector &inList); + void VisitAlloca(AddrShift gate); + void VisitBlock(int id, const OperandsVector &predecessors); + void VisitGoto(int block, int bbout); + void VisitParameter(AddrShift gate) const; + void VisitInt32Constant(AddrShift gate, int32_t value) const; + void VisitInt64Constant(AddrShift gate, int64_t value) const; + void VisitFloat64Constant(AddrShift gate, int64_t value) const; + void VisitZExtInt(AddrShift gate, AddrShift e1, MachineRep rep) const; + void VisitSExtInt(AddrShift gate, AddrShift e1, MachineRep rep) const; + void VisitLoad(AddrShift gate, MachineRep rep, AddrShift base) const; + void VisitStore(AddrShift gate, MachineRep rep, AddrShift base, AddrShift value) const; + void VisitIntRev(AddrShift gate, AddrShift e1) const; + void VisitIntAdd(AddrShift gate, AddrShift e1, AddrShift e2, MachineRep rep) const; + void VisitFloatAdd(AddrShift gate, AddrShift e1, AddrShift e2) const; + void VisitFloatSub(AddrShift gate, AddrShift e1, AddrShift e2) const; + void VisitFloatMul(AddrShift gate, AddrShift e1, AddrShift e2) const; + void VisitFloatDiv(AddrShift gate, AddrShift e1, AddrShift e2) const; + void VisitIntSub(AddrShift gate, AddrShift e1, AddrShift e2) const; + void VisitIntMul(AddrShift gate, AddrShift e1, AddrShift e2) const; + void VisitIntOr(AddrShift gate, AddrShift e1, AddrShift e2) const; + void VisitIntAnd(AddrShift gate, AddrShift e1, AddrShift e2) const; + void VisitIntLsr(AddrShift gate, AddrShift e1, AddrShift e2) const; + void VisitInt32LessThanOrEqual(AddrShift gate, AddrShift e1, AddrShift e2) const; + void VisitIntOrUintCmp(AddrShift gate, AddrShift e1, AddrShift e2, LLVMIntPredicate opcode) const; + void VisitFloatOrDoubleCmp(AddrShift gate, AddrShift e1, AddrShift e2, LLVMRealPredicate opcode) const; + void VisitBranch(AddrShift gate, AddrShift cmp, AddrShift btrue, AddrShift bfalse); + void VisitSwitch(AddrShift gate, AddrShift input, const std::vector &outList); + void VisitSwitchCase(AddrShift gate, AddrShift switchBranch, AddrShift out); + void VisitPhi(AddrShift gate, const std::vector &srcGates, MachineRep rep); + void VisitReturn(AddrShift gate, AddrShift popCount, const std::vector &operands) const; + void VisitCastIntXToIntY(AddrShift gate, AddrShift e1, MachineRep rep) const; + void VisitCastIntToDouble(AddrShift gate, AddrShift e1) const; + void VisitCastDoubleToInt(AddrShift gate, AddrShift e1) const; + void VisitCastInt64ToPointer(AddrShift gate, AddrShift e1) const; + + BasicBlock *EnsurBasicBlock(int id); + LLVMBasicBlockRef EnsureLLVMBB(BasicBlock *bb) const; + LLVMTFBuilderBasicBlockImpl *EnsureLLVMBBImpl(BasicBlock *bb) const; + void StartLLVMBuilder(BasicBlock *bb) const; + + LLVMTypeRef GetMachineRepType(MachineRep rep) const; + int FindBasicBlock(AddrShift gate) const; + void EndCurrentBlock() const; + void End(); + + void ProcessPhiWorkList(); + +private: + const std::vector> *m_schedule {nullptr}; + const Circuit *m_circuit {nullptr}; + BasicBlock *m_currentBb {nullptr}; + int m_lineNumber {0}; + + LLVMModuleRef m_module {nullptr}; + LLVMContextRef m_context; + LLVMValueRef m_function {nullptr}; + LLVMBuilderRef m_builder {nullptr}; + std::map m_instIdMapBbId; + BasicBlockMap m_bbIdMapBb; + + std::vector m_phiRebuildWorklist; +}; +#endif +} // namespace kungfu \ No newline at end of file diff --git a/ecmascript/compiler/llvm_mcjit_compiler.cpp b/ecmascript/compiler/llvm_mcjit_compiler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..07a9a0c2195a47f77d9525924c35a8ac90d5234b --- /dev/null +++ b/ecmascript/compiler/llvm_mcjit_compiler.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "llvm_mcjit_compiler.h" + +#include + +#include "llvm-c/Analysis.h" +#include "llvm-c/Core.h" +#include "llvm-c/Disassembler.h" +#include "llvm-c/DisassemblerTypes.h" +#include "llvm-c/Target.h" +#include "llvm-c/Transforms/PassManagerBuilder.h" +#include "llvm-c/Transforms/Scalar.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/IR/Argument.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" + +namespace kungfu { +static uint8_t *RoundTripAllocateCodeSection(void *object, uintptr_t size, unsigned alignment, unsigned sectionID, + const char *sectionName) +{ + std::cout << "RoundTripAllocateCodeSection object " << object << " - " << std::endl; + struct CompilerState& state = *static_cast(object); + uint8_t *addr = state.AllocaCodeSection(size, sectionName); + std::cout << "RoundTripAllocateCodeSection addr:" << std::hex << reinterpret_cast(addr) << addr + << " size:0x" << size << " +" << std::endl; + return addr; +} + +static uint8_t *RoundTripAllocateDataSection(void *object, uintptr_t size, unsigned alignment, unsigned sectionID, + const char *sectionName, LLVMBool isReadOnly) +{ + struct CompilerState& state = *static_cast(object); + return state.AllocaDataSection(size, sectionName); +} + +static LLVMBool RoundTripFinalizeMemory(void *object, char **errMsg) +{ + std::cout << "RoundTripFinalizeMemory object " << object << " - " << std::endl; + return 0; +} + +static void RoundTripDestroy(void *object) +{ + std::cout << "RoundTripDestroy object " << object << " - " << std::endl; + delete static_cast(object); +} + +void LLVMMCJITCompiler::UseRoundTripSectionMemoryManager() +{ + auto sectionMemoryManager = std::make_unique(); + m_options.MCJMM = + LLVMCreateSimpleMCJITMemoryManager(&compilerState, RoundTripAllocateCodeSection, + RoundTripAllocateDataSection, RoundTripFinalizeMemory, RoundTripDestroy); +} + +bool LLVMMCJITCompiler::BuildMCJITEngine() +{ + std::cout << " BuildMCJITEngine - " << std::endl; + LLVMBool ret = LLVMCreateMCJITCompilerForModule(&m_engine, m_module, &m_options, sizeof(m_options), &m_error); + std::cout << " m_engine " << m_engine << std::endl; + if (ret) { + std::cout << "m_error : " << m_error << std::endl; + return false; + } + std::cout << " BuildMCJITEngine ++++++++++++ " << std::endl; + return true; +} + +void LLVMMCJITCompiler::BuildAndRunPasses() const +{ + std::cout << "BuildAndRunPasses - " << std::endl; + LLVMPassManagerRef pass = LLVMCreatePassManager(); + LLVMAddConstantPropagationPass(pass); + LLVMAddInstructionCombiningPass(pass); + LLVMRunPassManager(pass, m_module); + LLVMDisposePassManager(pass); + std::cout << "BuildAndRunPasses + " << std::endl; +} + +LLVMMCJITCompiler::LLVMMCJITCompiler(LLVMModuleRef module): m_module(module), m_engine(nullptr), + m_hostTriple(""), m_error(nullptr) +{ + Initialize(); + InitMember(); +} + +LLVMMCJITCompiler::~LLVMMCJITCompiler() +{ + m_module = nullptr; + m_engine = nullptr; + m_hostTriple = ""; + m_error = nullptr; +} + +void LLVMMCJITCompiler::Run() +{ + UseRoundTripSectionMemoryManager(); + if (!BuildMCJITEngine()) { + return; + } + BuildAndRunPasses(); +} + +void LLVMMCJITCompiler::Initialize() +{ + LLVMInitializeAllTargetInfos(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllDisassemblers(); + /* this method must be called, ohterwise "Target does not support MC emission" */ + LLVMInitializeNativeAsmPrinter(); + LLVMInitializeNativeAsmParser(); + LLVMInitializeAllTargets(); + LLVMInitializeMCJITCompilerOptions(&m_options, sizeof(m_options)); + m_options.OptLevel = 2; // opt level 2 + // Just ensure that this field still exists. + m_options.NoFramePointerElim = false; +} + +static const char *SymbolLookupCallback(void *disInfo, uint64_t referenceValue, uint64_t *referenceType, + uint64_t referencePC, const char **referenceName) +{ + *referenceType = LLVMDisassembler_ReferenceType_InOut_None; + return nullptr; +} + +void LLVMMCJITCompiler::Disassemble() const +{ + LLVMDisasmContextRef dcr = LLVMCreateDisasm("x86_64-unknown-linux-gnu", nullptr, 0, nullptr, SymbolLookupCallback); + std::cout << "========================================================================" << std::endl; + for (auto it : compilerState.codeInfo) { + uint8_t *byteSp; + uintptr_t numBytes; + byteSp = it.first; + numBytes = it.second; + std::cout << " byteSp:" << std::hex << reinterpret_cast(byteSp) << " numBytes:0x" << numBytes + << std::endl; + + unsigned pc = 0; + const char outStringSize = 100; + char outString[outStringSize]; + while (numBytes != 0) { + size_t InstSize = LLVMDisasmInstruction(dcr, byteSp, numBytes, pc, outString, outStringSize); + if (InstSize == 0) { + fprintf(stderr, "%08x: %08x maybe constant\n", pc, *reinterpret_cast(byteSp)); + pc += 4; // 4 pc length + byteSp += 4; // 4 sp offset + numBytes -= 4; // 4 num bytes + } + + fprintf(stderr, "%08x: %08x %s\n", pc, *reinterpret_cast(byteSp), outString); + pc += InstSize; + byteSp += InstSize; + numBytes -= InstSize; + } + } + std::cout << "========================================================================" << std::endl; +} + +void LLVMMCJITCompiler::InitMember() +{ + if (m_module == nullptr) { + m_module = LLVMModuleCreateWithName("simple_module"); + LLVMSetTarget(m_module, "x86_64-unknown-linux-gnu"); + } +} +} // namespace kungfu diff --git a/ecmascript/compiler/llvm_mcjit_compiler.h b/ecmascript/compiler/llvm_mcjit_compiler.h new file mode 100644 index 0000000000000000000000000000000000000000..1da6ecfbad24a46ac1d5e7c9e3a582dcd85dfebd --- /dev/null +++ b/ecmascript/compiler/llvm_mcjit_compiler.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_LLVM_MCJINT_COMPILER_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_LLVM_MCJINT_COMPILER_H + +#include +#include +#include +#include + +#include "llvm/ExecutionEngine/Interpreter.h" +#include "llvm/ExecutionEngine/MCJIT.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" + +#include "llvm-c/Analysis.h" +#include "llvm-c/Core.h" +#include "llvm-c/ExecutionEngine.h" +#include "llvm-c/Target.h" +#include "llvm-c/Transforms/PassManagerBuilder.h" +#include "llvm-c/Transforms/Scalar.h" + +namespace kungfu { +using ByteBuffer = std::vector; +using BufferList = std::list; +using StringList = std::list; +struct CompilerState { + CompilerState(): machineCode(nullptr), codeBufferPos(0) + { + Reset(); + static constexpr int prot = PROT_READ | PROT_WRITE | PROT_EXEC; // NOLINT(hicpp-signed-bitwise) + static constexpr int flags = MAP_ANONYMOUS | MAP_SHARED; // NOLINT(hicpp-signed-bitwise) + machineCode = static_cast(mmap(nullptr, MAX_MACHINE_CODE_SIZE, prot, flags, -1, 0)); + } + ~CompilerState() + { + Reset(); + munmap(machineCode, MAX_MACHINE_CODE_SIZE); + } + uint8_t* AllocaCodeSection(uintptr_t size, const char* sectionName) + { + uint8_t *addr = nullptr; + if (codeBufferPos + size > MAX_MACHINE_CODE_SIZE) { + std::cerr << std::hex << "AllocaCodeSection failed alloc codeBufferPos:" << codeBufferPos << " size:" + << size << " larger MAX_MACHINE_CODE_SIZE:" << MAX_MACHINE_CODE_SIZE << std::endl; + return nullptr; + } + std::cout << "AllocaCodeSection size:" << size << std::endl; + std::vector codeBuffer(machineCode[codeBufferPos], size); + std::cout << " codeBuffer size: " << codeBuffer.size() << std::endl; + codeBufferPos += size; + codeSectionNames_.push_back(sectionName); + addr = machineCode + codeBufferPos; + std::cout << "AllocaCodeSection addr:" << std::hex << reinterpret_cast(addr) << std::endl; + codeInfo.push_back({addr, size}); + return addr; + } + + uint8_t* AllocaDataSection(uintptr_t size, const char* sectionName) + { + uint8_t *addr = nullptr; + dataSectionList_.push_back(std::vector()); + dataSectionList_.back().resize(size); + dataSectionNames_.push_back(sectionName); + addr = static_cast(dataSectionList_.back().data()); + return addr; + } + + void Reset() + { + codeInfo.clear(); + dataSectionList_.clear(); + dataSectionNames_.clear(); + codeSectionNames_.clear(); + codeBufferPos = 0; + } +public: + /* for asssembler */ + std::vector> codeInfo {}; +private: + BufferList dataSectionList_ {}; + StringList dataSectionNames_ {}; + StringList codeSectionNames_ {}; + uint8_t *machineCode; + const size_t MAX_MACHINE_CODE_SIZE = (1 << 20); // 1M + int codeBufferPos = 0; +}; +class LLVMMCJITCompiler { +public: + explicit LLVMMCJITCompiler(LLVMModuleRef module); + virtual ~LLVMMCJITCompiler(); + void Run(); + const LLVMExecutionEngineRef &GetEngine() + { + return m_engine; + } + void Disassemble() const; + +private: + void UseRoundTripSectionMemoryManager(); + bool BuildMCJITEngine(); + void BuildAndRunPasses() const; + void BuildSimpleFunction(); + void Initialize(); + void InitMember(); + + LLVMMCJITCompilerOptions m_options; + LLVMModuleRef m_module; + LLVMExecutionEngineRef m_engine; + std::string m_hostTriple; + char *m_error; + struct CompilerState compilerState; +}; + +#endif +} // namespace kungfu diff --git a/ecmascript/compiler/machine_type.h b/ecmascript/compiler/machine_type.h new file mode 100644 index 0000000000000000000000000000000000000000..01b45da8bfa08ff93d840cbb9b1c6f58f1354d1e --- /dev/null +++ b/ecmascript/compiler/machine_type.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_MACHINE_TYPE_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_MACHINE_TYPE_H + +namespace kungfu { +enum MachineType { + NONE_TYPE, + BOOL_TYPE, + INT8_TYPE, + INT16_TYPE, + INT32_TYPE, + INT64_TYPE, + UINT8_TYPE, + UINT16_TYPE, + UINT32_TYPE, + UINT64_TYPE, + POINTER_TYPE, + TAGGED_TYPE, + TAGGED_POINTER_TYPE, + FLOAT32_TYPE, + FLOAT64_TYPE, +}; +} // namespace kungfu +#endif // PANDA_RUNTIME_ECMASCRIPT_COMPILER_MACHINE_TYPE_H \ No newline at end of file diff --git a/ecmascript/compiler/scheduler.cpp b/ecmascript/compiler/scheduler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc678364c829e44a1ba9576356d3e07bf5ef28fb --- /dev/null +++ b/ecmascript/compiler/scheduler.cpp @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/compiler/scheduler.h" + +#include "ecmascript/compiler/verifier.h" + +namespace kungfu { +using DominatorTreeInfo = std::tuple, std::unordered_map, + std::vector>; +DominatorTreeInfo Scheduler::CalculateDominatorTree(const Circuit *circuit) +{ + std::vector bbGatesList; + std::unordered_map bbGatesAddrToIdx; + std::unordered_map dfsTimestamp; + circuit->AdvanceTime(); + { + size_t timestamp = 0; + std::deque pendingList; + auto startGate = Circuit::GetCircuitRoot(OpCode(OpCode::STATE_ENTRY)); + circuit->SetMark(startGate, MarkCode::VISITED); + pendingList.push_back(startGate); + while (!pendingList.empty()) { + auto curGate = pendingList.back(); + dfsTimestamp[curGate] = timestamp++; + pendingList.pop_back(); + bbGatesList.push_back(curGate); + if (circuit->GetOpCode(curGate) != OpCode::LOOP_BACK) { + for (const auto &succGate : circuit->GetOutVector(curGate)) { + if (circuit->GetOpCode(succGate).IsState() && circuit->GetMark(succGate) == MarkCode::EMPTY) { + circuit->SetMark(succGate, MarkCode::VISITED); + pendingList.push_back(succGate); + } + } + } + } + for (size_t idx = 0; idx < bbGatesList.size(); idx++) { + bbGatesAddrToIdx[bbGatesList[idx]] = idx; + } + } + std::vector immDom(bbGatesList.size()); + { + std::vector> dom(bbGatesList.size()); + dom[0] = {0}; + for (size_t idx = 1; idx < dom.size(); idx++) { + dom[idx].resize(dom.size()); + std::iota(dom[idx].begin(), dom[idx].end(), 0); + } + bool changed = true; + while (changed) { + changed = false; + for (size_t idx = 1; idx < dom.size(); idx++) { + auto &curDom = dom[idx]; + size_t origSize = curDom.size(); + curDom.resize(dom.size()); + std::iota(curDom.begin(), curDom.end(), 0); + for (const auto &predGate : circuit->GetInVector(bbGatesList[idx])) { + if (bbGatesAddrToIdx.count(predGate) > 0) { + std::vector tmp(curDom.size()); + const auto &predDom = dom[bbGatesAddrToIdx[predGate]]; + auto it = std::set_intersection( + curDom.begin(), curDom.end(), predDom.begin(), predDom.end(), tmp.begin()); + tmp.resize(it - tmp.begin()); + curDom = tmp; + } + } + auto it = std::find(curDom.begin(), curDom.end(), idx); + if (it == curDom.end()) { + curDom.push_back(idx); + std::sort(curDom.begin(), curDom.end()); + } + if (dom[idx].size() != origSize) { + changed = true; + } + } + } + immDom[0] = dom[0].front(); + for (size_t idx = 1; idx < dom.size(); idx++) { + auto it = std::remove(dom[idx].begin(), dom[idx].end(), idx); + dom[idx].resize(it - dom[idx].begin()); + immDom[idx] = *std::max_element(dom[idx].begin(), dom[idx].end(), + [bbGatesList, dfsTimestamp](const size_t &lhs, const size_t &rhs) -> bool { + return dfsTimestamp.at(bbGatesList[lhs]) < dfsTimestamp.at(bbGatesList[rhs]); + }); + } + } + return {bbGatesList, bbGatesAddrToIdx, immDom}; +} + +std::vector> Scheduler::Run(const Circuit *circuit) +{ +#ifndef NDEBUG + if (!Verifier::Run(circuit)) { + UNREACHABLE(); + } +#endif + std::vector bbGatesList; + std::unordered_map bbGatesAddrToIdx; + std::vector immDom; + std::tie(bbGatesList, bbGatesAddrToIdx, immDom) = Scheduler::CalculateDominatorTree(circuit); + std::vector> result(bbGatesList.size()); + for (size_t idx = 0; idx < bbGatesList.size(); idx++) { + result[idx].push_back(bbGatesList[idx]); + } + // assuming CFG is always reducible + std::vector> sonList(result.size()); + for (size_t idx = 1; idx < immDom.size(); idx++) { + sonList[immDom[idx]].push_back(idx); + } + const size_t sizeLog = ceil(log2(static_cast(result.size())) + 1); + std::vector timeIn(result.size()); + std::vector timeOut(result.size()); + std::vector> jumpUp; + jumpUp.assign(result.size(), std::vector(sizeLog + 1)); + { + size_t timestamp = 0; + std::function dfs = [&](size_t cur, size_t prev) { + timeIn[cur] = timestamp; + timestamp++; + jumpUp[cur][0] = prev; + for (size_t stepSize = 1; stepSize <= sizeLog; stepSize++) { + jumpUp[cur][stepSize] = jumpUp[jumpUp[cur][stepSize - 1]][stepSize - 1]; + } + for (const auto &succ : sonList[cur]) { + dfs(succ, cur); + } + timeOut[cur] = timestamp; + timestamp++; + }; + size_t root = 0; + dfs(root, root); + } + auto isAncestor = [&](size_t nodeA, size_t nodeB) -> bool { + return (timeIn[nodeA] <= timeIn[nodeB]) && (timeOut[nodeA] >= timeOut[nodeB]); + }; + auto lowestCommonAncestor = [&](size_t nodeA, size_t nodeB) -> size_t { + if (isAncestor(nodeA, nodeB)) { + return nodeA; + } + if (isAncestor(nodeB, nodeA)) { + return nodeB; + } + for (size_t stepSize = sizeLog + 1; stepSize > 0; stepSize--) { + if (!isAncestor(jumpUp[nodeA][stepSize - 1], nodeB)) { + nodeA = jumpUp[nodeA][stepSize - 1]; + } + } + return jumpUp[nodeA][0]; + }; + { + std::vector order; + auto lowerBound = + Scheduler::CalculateSchedulingLowerBound(circuit, bbGatesAddrToIdx, lowestCommonAncestor, &order).value(); + for (const auto &schedulableGate : order) { + result[lowerBound.at(schedulableGate)].push_back(schedulableGate); + } + auto argList = circuit->GetOutVector(Circuit::GetCircuitRoot(OpCode(OpCode::ARG_LIST))); + std::sort(argList.begin(), argList.end(), [&](const AddrShift &lhs, const AddrShift &rhs) -> bool { + return circuit->GetBitField(lhs) > circuit->GetBitField(rhs); + }); + for (const auto &arg : argList) { + result.front().push_back(arg); + } + for (const auto &bbGate : bbGatesList) { + for (const auto &succGate : circuit->GetOutVector(bbGate)) { + if (circuit->GetOpCode(succGate).IsFixed()) { + result[bbGatesAddrToIdx.at(circuit->GetIn(succGate, 0))].push_back(succGate); + } + } + } + } + return result; +} + +std::optional> Scheduler::CalculateSchedulingUpperBound(const Circuit *circuit, + const std::unordered_map &bbGatesAddrToIdx, + const std::function &isAncestor, const std::vector &schedulableGatesList) +{ + std::unordered_map upperBound; + std::function(AddrShift)> dfs = [&](AddrShift curGate) -> std::optional { + if (upperBound.count(curGate) > 0) { + return upperBound[curGate]; + } + if (circuit->GetOpCode(curGate).IsProlog() || circuit->GetOpCode(curGate).IsRoot()) { + return 0; + } + if (circuit->GetOpCode(curGate).IsFixed()) { + return bbGatesAddrToIdx.at(circuit->GetIn(curGate, 0)); + } + // then cur is schedulable + size_t curUpperBound = 0; + for (const auto &predGate : circuit->GetInVector(curGate)) { + auto predResult = dfs(predGate); + if (!predResult.has_value()) { + return std::nullopt; + } + auto predUpperBound = predResult.value(); + if (!isAncestor(curUpperBound, predUpperBound) && !isAncestor(predUpperBound, curUpperBound)) { + std::cerr << "[Verifier][Error] Scheduling upper bound of gate (id=" << curGate << ") does not exist" + << std::endl; + return std::nullopt; + } + if (isAncestor(curUpperBound, predUpperBound)) { + curUpperBound = predUpperBound; + } + } + return (upperBound[curGate] = curUpperBound); + }; + for (const auto &schedulableGate : schedulableGatesList) { + if (upperBound.count(schedulableGate) == 0) { + if (!dfs(schedulableGate).has_value()) { + return std::nullopt; + } + } + } + return upperBound; +} + +std::optional> Scheduler::CalculateSchedulingLowerBound(const Circuit *circuit, + const std::unordered_map &bbGatesAddrToIdx, + const std::function &lowestCommonAncestor, std::vector *order) +{ + std::unordered_map lowerBound; + std::unordered_map useCount; + std::deque pendingList; + std::vector bbAndFixedGatesList; + for (const auto &item : bbGatesAddrToIdx) { + bbAndFixedGatesList.push_back(item.first); + for (const auto &succGate : circuit->GetOutVector(item.first)) { + if (circuit->GetOpCode(succGate).IsFixed()) { + bbAndFixedGatesList.push_back(succGate); + } + } + } + std::function dfsVisit = [&](AddrShift curGate) { + for (const auto &prevGate : circuit->GetInVector(curGate)) { + if (circuit->GetOpCode(prevGate).IsSchedulable()) { + useCount[prevGate]++; + if (useCount[prevGate] == 1) { + dfsVisit(prevGate); + } + } + } + }; + for (const auto &gate : bbAndFixedGatesList) { + dfsVisit(gate); + } + std::function dfsFinish = [&](AddrShift curGate) { + size_t cnt = 0; + for (const auto &prevGate : circuit->GetInVector(curGate)) { + if (circuit->GetOpCode(prevGate).IsSchedulable()) { + useCount[prevGate]--; + size_t curLowerBound; + if (circuit->GetOpCode(curGate).IsState()) { // cur_opcode would not be STATE_ENTRY + curLowerBound = bbGatesAddrToIdx.at(curGate); + } else if (circuit->GetOpCode(curGate).IsFixed()) { + ASSERT(cnt > 0); + curLowerBound = bbGatesAddrToIdx.at(circuit->GetIn(circuit->GetIn(curGate, 0), cnt - 1)); + } else { + curLowerBound = lowerBound.at(curGate); + } + if (lowerBound.count(prevGate) == 0) { + lowerBound[prevGate] = curLowerBound; + } else { + lowerBound[prevGate] = lowestCommonAncestor(lowerBound[prevGate], curLowerBound); + } + if (useCount[prevGate] == 0) { + if (order != nullptr) { + order->push_back(prevGate); + } + dfsFinish(prevGate); + } + } + cnt++; + } + }; + for (const auto &gate : bbAndFixedGatesList) { + dfsFinish(gate); + } + return lowerBound; +} + +void Scheduler::Print(const std::vector> *cfg, const Circuit *circuit) +{ + std::vector bbGatesList; + std::unordered_map bbGatesAddrToIdx; + std::vector immDom; + std::tie(bbGatesList, bbGatesAddrToIdx, immDom) = Scheduler::CalculateDominatorTree(circuit); + std::cout << "==========================================================================" << std::endl; + for (size_t bbIdx = 0; bbIdx < cfg->size(); bbIdx++) { + std::cout << "BB_" << bbIdx << "_" << circuit->GetOpCode((*cfg)[bbIdx].front()).Str() << ":" + << " immDom=" << immDom[bbIdx]; + std::cout << " pred=["; + bool isFirst = true; + for (const auto &predStates : circuit->GetInVector((*cfg)[bbIdx].front())) { + if (circuit->GetOpCode(predStates).IsState() || circuit->GetOpCode(predStates) == OpCode::STATE_ENTRY) { + std::cout << (isFirst ? "" : " ") << bbGatesAddrToIdx.at(predStates); + isFirst = false; + } + } + std::cout << "] succ=["; + isFirst = true; + for (const auto &succStates : circuit->GetOutVector((*cfg)[bbIdx].front())) { + if (circuit->GetOpCode(succStates).IsState() || circuit->GetOpCode(succStates) == OpCode::STATE_ENTRY) { + std::cout << (isFirst ? "" : " ") << bbGatesAddrToIdx.at(succStates); + isFirst = false; + } + } + std::cout << "]"; + std::cout << std::endl; + for (size_t instIdx = (*cfg)[bbIdx].size(); instIdx > 0; instIdx--) { + circuit->Print((*cfg)[bbIdx][instIdx - 1]); + } + } + std::cout << "==========================================================================" << std::endl; +} +} // namespace kungfu \ No newline at end of file diff --git a/ecmascript/compiler/scheduler.h b/ecmascript/compiler/scheduler.h new file mode 100644 index 0000000000000000000000000000000000000000..cf5e2d4bb2b7ee970b55880a853c819f13231d00 --- /dev/null +++ b/ecmascript/compiler/scheduler.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_SCHEDULER_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_SCHEDULER_H + +#include +#include +#include +#include +#include + +#include "ecmascript/compiler/circuit.h" + +namespace kungfu { +class Scheduler { +public: + static std::tuple, std::unordered_map, std::vector> + CalculateDominatorTree(const Circuit *circuit); + static std::vector> Run(const Circuit *circuit); + static std::optional> CalculateSchedulingUpperBound(const Circuit *circuit, + const std::unordered_map &bbGatesAddrToIdx, + const std::function &isAncestor, const std::vector &schedulableGatesList); + static std::optional> CalculateSchedulingLowerBound(const Circuit *circuit, + const std::unordered_map &bbGatesAddrToIdx, + const std::function &lowestCommonAncestor, std::vector *order = nullptr); + static void Print(const std::vector> *cfg, const Circuit *circuit); +}; +}; // namespace kungfu + +#endif // PANDA_RUNTIME_ECMASCRIPT_COMPILER_SCHEDULER_H diff --git a/ecmascript/compiler/stub_interface.cpp b/ecmascript/compiler/stub_interface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8fe7e2594c9538718e0e18613247dfa4f836b5d4 --- /dev/null +++ b/ecmascript/compiler/stub_interface.cpp @@ -0,0 +1,437 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/compiler/stub_interface.h" + +#include "llvm-c/Core.h" +#include "llvm/Support/Host.h" + +namespace kungfu { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) + +#define LLVM_STUB_GETFUCTION(name) \ + class LLVM##name##Stub final { \ + public: \ + static LLVMValueRef InitializeFunction(LLVMModuleRef module); \ + }; \ + LLVMValueRef LLVM##name##Stub::InitializeFunction(LLVMModuleRef module) + +LLVM_STUB_GETFUCTION(FastAdd) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(FastSub) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(FastMul) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(FastDiv) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(FastMod) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(FastEqual) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(FastTypeOf) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(FastStrictEqual) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(IsSpecialIndexedObjForSet) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(IsSpecialIndexedObjForGet) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(GetElement) +{ + // 2 : 2 input parameters + std::array paramTys = { + LLVMInt64Type(), + LLVMInt32Type(), + }; + // 2 : 2 input parameters + return LLVMAddFunction(module, "GetElement", LLVMFunctionType(LLVMInt64Type(), paramTys.data(), 2, 0)); +} + +LLVM_STUB_GETFUCTION(SetElement) +{ + // 5 : 5 input parameters + std::array paramTys = { + LLVMInt64Type(), LLVMInt64Type(), LLVMInt32Type(), LLVMInt64Type(), LLVMInt32Type(), + }; + // 5 : 5 input parameters + return LLVMAddFunction(module, "SetElement", LLVMFunctionType(LLVMInt1Type(), paramTys.data(), 5, 0)); +} + +LLVM_STUB_GETFUCTION(SetPropertyByName) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(GetPropertyByName) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(SetGlobalOwnProperty) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(GetGlobalOwnProperty) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(SetOwnPropertyByName) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(SetOwnElement) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(FastSetProperty) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(FastGetProperty) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(FindOwnProperty) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(FindOwnElement) +{ + // 2 : 2 input parameters + std::array paramTys = { + LLVMInt64Type(), + LLVMInt32Type(), + }; + // 2 : 2 input parameters + return LLVMAddFunction(module, "FindOwnElement", LLVMFunctionType(LLVMInt64Type(), paramTys.data(), 2, 0)); +} + +LLVM_STUB_GETFUCTION(NewLexicalEnvDyn) +{ + (void)module; + return nullptr; +} + +LLVM_STUB_GETFUCTION(FindOwnProperty2) +{ + // 5 : 5 input parameters + std::array paramTys = { + LLVMInt64Type(), LLVMInt32Type(), LLVMInt1Type(), LLVMInt64Type(), LLVMInt64Type(), + }; + // 5 : 5 input parameters + return LLVMAddFunction(module, "FindOwnProperty2", LLVMFunctionType(LLVMInt64Type(), paramTys.data(), 5, 0)); +} + +LLVM_STUB_GETFUCTION(FindOwnElement2) +{ + // 5 : 5 input parameters + std::array paramTys = { + LLVMInt64Type(), LLVMInt32Type(), LLVMInt1Type(), LLVMInt64Type(), LLVMInt64Type(), + }; + // 5 : 5 input parameters + return LLVMAddFunction(module, "FindOwnElement2", LLVMFunctionType(LLVMInt64Type(), paramTys.data(), 5, 0)); +} + +LLVM_STUB_GETFUCTION(AddElementInternal) +{ + // 5 : 5 input parameters + std::array paramTys = { + LLVMInt64Type(), LLVMInt64Type(), LLVMInt32Type(), LLVMInt64Type(), LLVMInt32Type(), + }; + return LLVMAddFunction(module, "_ZN5panda10ecmascript11RuntimeStub18AddElementInternalEmmjmj", + LLVMFunctionType(LLVMInt1Type(), paramTys.data(), 5, 0)); // 5 : 5 input parameters +} + +LLVM_STUB_GETFUCTION(CallSetter) +{ + // 5 : 5 input parameters + std::array paramTys = { + LLVMInt64Type(), LLVMInt64Type(), LLVMInt64Type(), LLVMInt64Type(), LLVMInt1Type(), + }; + return LLVMAddFunction(module, "_ZN5panda10ecmascript11RuntimeStub10CallSetterEmmmmb", + LLVMFunctionType(LLVMInt1Type(), paramTys.data(), 5, 0)); // 5 : 5 input parameters +} + +LLVM_STUB_GETFUCTION(ThrowTypeError) +{ + // 2 : 2 input parameter + std::array paramTys = { + LLVMInt64Type(), + LLVMInt32Type(), + }; + return LLVMAddFunction(module, " _ZN5panda10ecmascript11RuntimeStub14ThrowTypeErrorEmi", + LLVMFunctionType(LLVMVoidType(), paramTys.data(), 2, 0)); // 2 : 2 input parameters +} + +LLVM_STUB_GETFUCTION(JSProxySetProperty) +{ + // 6 : 6 input parameters + std::array paramTys = { + LLVMInt64Type(), LLVMInt64Type(), LLVMInt64Type(), LLVMInt64Type(), LLVMInt64Type(), LLVMInt1Type(), + }; + return LLVMAddFunction(module, "_ZN5panda10ecmascript11RuntimeStub18JSProxySetPropertyEmmmmmb", + LLVMFunctionType(LLVMInt1Type(), paramTys.data(), 6, 0)); // 6 : 6 input parameters +} + +LLVM_STUB_GETFUCTION(GetHash32) +{ + std::array paramTys = { // 2 : 2 input parameters + LLVMInt64Type(), LLVMInt32Type()}; + return LLVMAddFunction(module, "_ZN5panda9GetHash32EPKhm", + LLVMFunctionType(LLVMInt32Type(), paramTys.data(), 2, 0)); // 2 : 2 input parameters +} + +LLVM_STUB_GETFUCTION(PhiTest) +{ + std::array paramTys = {LLVMInt32Type()}; + return LLVMAddFunction(module, "PhiTest", LLVMFunctionType(LLVMInt32Type(), paramTys.data(), 1, 0)); +} + +LLVMStubsImplement::LLVMStubsImplement() +{ + stubsModule_ = LLVMModuleCreateWithName("fast_stubs"); + LLVMSetTarget(stubsModule_, "x86_64-unknown-linux-gnu"); +} + +void LLVMStubsImplement::Initialize() +{ +// Initialize Stubs Function +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DEF_CALL_STUB(name) NAME_##name +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INITIALIZE_CALL_STUB(name, argcounts) \ + llvmCallStubs_[DEF_CALL_STUB(name)] = LLVM##name##Stub::InitializeFunction(stubsModule_); + CALL_STUB_LIST(INITIALIZE_CALL_STUB) +#undef INITIALIZE_CALL_STUB +#undef DEF_CALL_STUB +} + +void *LLVMStubsImplement::GetFastStub(int index) +{ + ASSERT(index < CALL_STUB_MAXCOUNT && index >= 0); + return reinterpret_cast(llvmCallStubs_[index]); +} + +void LLVMStubsImplement::SetFastStub(int index, void *code) +{ + ASSERT(index < CALL_STUB_MAXCOUNT && index >= 0); + llvmCallStubs_[index] = reinterpret_cast(code); +} + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CALL_STUB_INIT_DESCRIPTOR(name) \ + class Stub##name##InterfaceDescriptor final { \ + public: \ + static void Initialize(StubInterfaceDescriptor *descriptor); \ + }; \ + void Stub##name##InterfaceDescriptor::Initialize(StubInterfaceDescriptor *descriptor) + +CALL_STUB_INIT_DESCRIPTOR(FastAdd) {} + +CALL_STUB_INIT_DESCRIPTOR(FastSub) {} + +CALL_STUB_INIT_DESCRIPTOR(FastMul) {} + +CALL_STUB_INIT_DESCRIPTOR(FastDiv) {} + +CALL_STUB_INIT_DESCRIPTOR(FastMod) {} + +CALL_STUB_INIT_DESCRIPTOR(FastEqual) {} + +CALL_STUB_INIT_DESCRIPTOR(FastTypeOf) {} + +CALL_STUB_INIT_DESCRIPTOR(FastStrictEqual) {} + +CALL_STUB_INIT_DESCRIPTOR(IsSpecialIndexedObjForSet) {} + +CALL_STUB_INIT_DESCRIPTOR(IsSpecialIndexedObjForGet) {} + +CALL_STUB_INIT_DESCRIPTOR(GetElement) {} + +CALL_STUB_INIT_DESCRIPTOR(SetElement) {} + +CALL_STUB_INIT_DESCRIPTOR(SetPropertyByName) {} + +CALL_STUB_INIT_DESCRIPTOR(GetPropertyByName) {} + +CALL_STUB_INIT_DESCRIPTOR(SetGlobalOwnProperty) {} + +CALL_STUB_INIT_DESCRIPTOR(GetGlobalOwnProperty) {} + +CALL_STUB_INIT_DESCRIPTOR(SetOwnPropertyByName) {} + +CALL_STUB_INIT_DESCRIPTOR(SetOwnElement) {} + +CALL_STUB_INIT_DESCRIPTOR(FastSetProperty) {} + +CALL_STUB_INIT_DESCRIPTOR(FastGetProperty) {} + +CALL_STUB_INIT_DESCRIPTOR(FindOwnProperty) {} + +CALL_STUB_INIT_DESCRIPTOR(FindOwnElement) {} + +CALL_STUB_INIT_DESCRIPTOR(NewLexicalEnvDyn) {} + +CALL_STUB_INIT_DESCRIPTOR(FindOwnProperty2) {} + +CALL_STUB_INIT_DESCRIPTOR(FindOwnElement2) +{ + // 5 : 5 input parameters + static StubInterfaceDescriptor findOwnElement2(0, 5, DEFAULT_ORDER, UINT64_TYPE); + *descriptor = findOwnElement2; + auto params = new std::array(); // 5 : 5 input parameters + (*params)[0] = MachineType::UINT64_TYPE; + (*params)[1] = MachineType::UINT32_TYPE; + (*params)[2] = MachineType::BOOL_TYPE; // 2 : 3rd para + (*params)[3] = MachineType::UINT64_TYPE; // 3 : 4th para + (*params)[4] = MachineType::UINT64_TYPE; // 4 : 5th para + descriptor->SetParameters(params->data()); +} + +CALL_STUB_INIT_DESCRIPTOR(AddElementInternal) +{ + // 5 : 5 input parameters + static StubInterfaceDescriptor addElementInternal(0, 5, DEFAULT_ORDER, BOOL_TYPE); + *descriptor = addElementInternal; + auto params = new std::array(); // 5 : 5 input parameters + (*params)[0] = MachineType::UINT64_TYPE; + (*params)[1] = MachineType::UINT64_TYPE; + (*params)[2] = MachineType::UINT32_TYPE; // 2 : 3rd para + (*params)[3] = MachineType::UINT64_TYPE; // 3 : 4th para + (*params)[4] = MachineType::UINT32_TYPE; // 4 : 5th para + descriptor->SetParameters(params->data()); +} + +CALL_STUB_INIT_DESCRIPTOR(CallSetter) +{ + // 5 : 5 input parameters + static StubInterfaceDescriptor callSetter(0, 5, DEFAULT_ORDER, NONE_TYPE); + *descriptor = callSetter; + auto params = new std::array(); // 5 : 5 input parameters + (*params)[0] = MachineType::UINT64_TYPE; + (*params)[1] = MachineType::UINT64_TYPE; + (*params)[2] = MachineType::UINT64_TYPE; // 2 : 3rd para + (*params)[3] = MachineType::UINT64_TYPE; // 3 : 4th para + (*params)[4] = MachineType::BOOL_TYPE; // 4 : 5th para + descriptor->SetParameters(params->data()); +} + +CALL_STUB_INIT_DESCRIPTOR(ThrowTypeError) +{ + // 2 : 2 input parameters + static StubInterfaceDescriptor throwTypeError(0, 2, DEFAULT_ORDER, NONE_TYPE); + *descriptor = throwTypeError; + auto params = new std::array(); // 2 : 2 input parameters + (*params)[0] = MachineType::UINT64_TYPE; + (*params)[1] = MachineType::UINT32_TYPE; + descriptor->SetParameters(params->data()); +} + +CALL_STUB_INIT_DESCRIPTOR(JSProxySetProperty) +{ + // 6 : 6 input parameters + static StubInterfaceDescriptor jsproxySetproperty(0, 6, DEFAULT_ORDER, BOOL_TYPE); + *descriptor = jsproxySetproperty; + auto params = new std::array(); // 6 : 6 input parameters + (*params)[0] = MachineType::UINT64_TYPE; + (*params)[1] = MachineType::UINT64_TYPE; + (*params)[2] = MachineType::UINT64_TYPE; // 2 : 3rd para + (*params)[3] = MachineType::UINT64_TYPE; // 3 : 4th para + (*params)[4] = MachineType::UINT64_TYPE; // 4 : 5th para + (*params)[5] = MachineType::BOOL_TYPE; // 5 : 6th para + descriptor->SetParameters(params->data()); +} + +CALL_STUB_INIT_DESCRIPTOR(GetHash32) +{ + // 2 : 2 input parameters + static StubInterfaceDescriptor getHash32(0, 2, DEFAULT_ORDER, UINT32_TYPE); + *descriptor = getHash32; + auto params = new std::array(); // 2 : 2 input parameters + (*params)[0] = MachineType::POINTER_TYPE; + (*params)[1] = MachineType::UINT32_TYPE; + descriptor->SetParameters(params->data()); +} + +CALL_STUB_INIT_DESCRIPTOR(PhiTest) {} + +void FastStubs::InitializeStubDescriptors() +{ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DEF_CALL_STUB(name) CallStubsImplement::NAME_##name +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INITIALIZE_CALL_STUB_DESCRIPTOR(name, argcounts) \ + Stub##name##InterfaceDescriptor::Initialize(&callStubsDescriptor_[DEF_CALL_STUB(name)]); + CALL_STUB_LIST(INITIALIZE_CALL_STUB_DESCRIPTOR) +#undef INITIALIZE_CALL_STUB_DESCRIPTOR +#undef DEF_CALL_STUB +} +} // namespace kungfu diff --git a/ecmascript/compiler/stub_interface.h b/ecmascript/compiler/stub_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..35910a59dca9e9766a252ba393161a93a81f7549 --- /dev/null +++ b/ecmascript/compiler/stub_interface.h @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_STUB_INTERFACE_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_STUB_INTERFACE_H + +#include "ecmascript/compiler/fast_stub_define.h" +#include "ecmascript/compiler/machine_type.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/mem/code.h" +#include "llvm-c/Types.h" + +namespace kungfu { +enum ArgumentsOrder { + DEFAULT_ORDER, // Push Arguments in stack from right -> left +}; + +class StubInterfaceDescriptor { +public: + explicit StubInterfaceDescriptor(int flags, int paramCounter, ArgumentsOrder order, MachineType returnType) + : flags_(flags), paramCounter_(paramCounter), order_(order), returnType_(returnType) + { + } + explicit StubInterfaceDescriptor(int flags, int paramCounter, ArgumentsOrder order, MachineType returnType, + MachineType *paramsType) + : flags_(flags), paramCounter_(paramCounter), order_(order), returnType_(returnType) + { + paramsType_ = paramsType; + } + StubInterfaceDescriptor() = default; + ~StubInterfaceDescriptor() + { + if (paramsType_ != nullptr) { + delete paramsType_; + } + } + + void SetParameters(MachineType *paramsType) + { + paramsType_ = paramsType; + } + + MachineType *GetParametersType() const + { + return paramsType_; + } + + int GetParametersCount() const + { + return paramCounter_; + } + + MachineType GetReturnType() const + { + return returnType_; + } + + ArgumentsOrder GetArgumentsOrder() const + { + return order_; + } + + int GetFlags() const + { + return flags_; + } + +private: + int flags_{0}; + int paramCounter_{0}; + ArgumentsOrder order_{DEFAULT_ORDER}; + + MachineType returnType_{MachineType::NONE_TYPE}; + MachineType *paramsType_{nullptr}; +}; + +class StubInterface final { +public: + explicit StubInterface(panda::ecmascript::JSHandle code, + StubInterfaceDescriptor *descriptor) + : code_(code), descriptor_(descriptor) + { + } + virtual ~StubInterface() = default; + DEFAULT_COPY_SEMANTIC(StubInterface); + DEFAULT_MOVE_SEMANTIC(StubInterface); + + panda::ecmascript::JSHandle GetCode() const + { + return code_; + } + + StubInterfaceDescriptor *GetDescriptor() const + { + return descriptor_; + } + +private: + panda::ecmascript::JSHandle code_; + StubInterfaceDescriptor *descriptor_; +}; + +class CallStubsImplement { +public: + CallStubsImplement() = default; + virtual ~CallStubsImplement() = default; + NO_MOVE_SEMANTIC(CallStubsImplement); + NO_COPY_SEMANTIC(CallStubsImplement); + enum CallStubName { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DEF_FAST_STUB(name, counter) NAME_##name, + CALL_STUB_LIST(DEF_FAST_STUB) +#undef DEF_FAST_STUB + CALL_STUB_MAXCOUNT, + }; + + virtual void Initialize() = 0; + virtual void *GetFastStub(int index) = 0; + virtual void SetFastStub(int index, void *code) = 0; + virtual void *GetModule() = 0; +}; + +class LLVMStubsImplement : public CallStubsImplement { +public: + LLVMStubsImplement(); + ~LLVMStubsImplement() override = default; + NO_MOVE_SEMANTIC(LLVMStubsImplement); + NO_COPY_SEMANTIC(LLVMStubsImplement); + void Initialize() override; + void *GetFastStub(int index) override; + void SetFastStub(int index, void *code) override; + void *GetModule() override + { + return reinterpret_cast(stubsModule_); + } + +private: + std::array llvmCallStubs_{nullptr}; + LLVMModuleRef stubsModule_{nullptr}; +}; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FAST_STUB_ID(name) CallStubsImplement::NAME_##name +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GET_STUBDESCRIPTOR(name) FastStubs::GetInstance().GetStubDescriptor(FAST_STUB_ID(name)) + +class FastStubs { +public: + static FastStubs &GetInstance() + { + static FastStubs instance; + return instance; + } + + void InitializeStubDescriptors(); + + void InitializeFastStubs() + { + InitializeStubDescriptors(); + stubsImpl_->Initialize(); + } + + void *GetFastStub(int index) const + { + return stubsImpl_->GetFastStub(index); + } + + void SetFastStub(int index, void *code) + { + return stubsImpl_->SetFastStub(index, code); + } + + void *GetModule() const + { + return stubsImpl_->GetModule(); + } + + StubInterfaceDescriptor *GetStubDescriptor(int index) + { + return &callStubsDescriptor_[index]; + } + +private: + FastStubs() + { + stubsImpl_ = std::make_unique(); + InitializeFastStubs(); + } + ~FastStubs() {} + NO_MOVE_SEMANTIC(FastStubs); + NO_COPY_SEMANTIC(FastStubs); + std::unique_ptr stubsImpl_{nullptr}; + std::array callStubsDescriptor_{}; +}; +} // namespace kungfu +#endif // PANDA_RUNTIME_ECMASCRIPT_COMPILER_STUB_INTERFACE_H \ No newline at end of file diff --git a/ecmascript/compiler/stub_optimizer.cpp b/ecmascript/compiler/stub_optimizer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8033befdf94b8822747ba8ceb962c1a78cd6fedc --- /dev/null +++ b/ecmascript/compiler/stub_optimizer.cpp @@ -0,0 +1,606 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/compiler/stub_optimizer.h" + +#include "ecmascript/compiler/llvm_ir_builder.h" +#include "ecmascript/js_object.h" +#include "ecmascript/tagged_hash_table-inl.h" +#include "libpandabase/macros.h" + +namespace kungfu { +using StubOPtimizerLabelImplement = StubOptimizerLabel::StubOptimizerLabelImplement; + +StubOptimizerLabel::StubOptimizerLabel(Environment *env) +{ + impl_ = env->NewStubOptimizerLabel(env); +} + +AddrShift StubVariable::AddPhiOperand(AddrShift val) +{ + ASSERT(IsSelector(val)); + StubOptimizerLabel label = env_->GetLabelFromSelector(val); + size_t idx = 0; + for (auto pred : label.GetPredecessors()) { + idx++; + val = AddOperandToSelector(val, idx, pred.ReadVariable(this)); + } + + return TryRemoveTrivialPhi(val); +} + +AddrShift StubVariable::AddOperandToSelector(AddrShift val, size_t idx, AddrShift in) +{ + env_->GetCircuit().NewIn(val, idx, in); + return val; +} + +AddrShift StubVariable::TryRemoveTrivialPhi(AddrShift phiVal) +{ + Gate *phi = env_->GetCircuit().LoadGatePtr(phiVal); + Gate *same = nullptr; + for (size_t i = 1; i < phi->GetNumIns(); ++i) { + In *phiIn = phi->GetIn(i); + Gate *op = (!phiIn->IsGateNull()) ? phiIn->GetGate() : nullptr; + if (op == same || op == phi) { + continue; // unique value or self-reference + } + if (same != nullptr) { + return phiVal; // the phi merges at least two values: not trivial + } + same = op; + } + if (same == nullptr) { + // the phi is unreachable or in the start block + same = env_->GetCircuit().LoadGatePtr(env_->GetCircuitBuilder().UndefineConstant()); + } + + // remove the trivial phi + // get all users of phi except self + std::vector outs; + if (!phi->IsFirstOutNull()) { + Out *phiOut = phi->GetFirstOut(); + while (!phiOut->IsNextOutNull()) { + if (phiOut->GetGate() != phi) { + // remove phi + outs.push_back(phiOut); + } + phiOut = phiOut->GetNextOut(); + } + // save last phi out + if (phiOut->GetGate() != phi) { + outs.push_back(phiOut); + } + } + // reroute all outs of phi to same and remove phi + RerouteOuts(outs, same); + phi->DeleteGate(); + + // try to recursiveby remove all phi users, which might have vecome trivial + for (auto out : outs) { + if (IsSelector(out->GetGate())) { + auto out_addr_shift = env_->GetCircuit().SaveGatePtr(out->GetGate()); + TryRemoveTrivialPhi(out_addr_shift); + } + } + return env_->GetCircuit().SaveGatePtr(same); +} + +void StubVariable::RerouteOuts(const std::vector &outs, Gate *newGate) +{ + // reroute all outs to new node + for (auto out : outs) { + size_t idx = out->GetIndex(); + out->GetGate()->ModifyIn(idx, newGate); + } +} + +void StubOPtimizerLabelImplement::Seal() +{ + for (auto &[variable, gate] : incompletePhis_) { + variable->AddPhiOperand(gate); + } + isSealed_ = true; +} + +void StubOPtimizerLabelImplement::WriteVariable(StubVariable *var, AddrShift value) +{ + valueMap_[var] = value; +} + +AddrShift StubOPtimizerLabelImplement::ReadVariable(StubVariable *var) +{ + if (valueMap_.find(var) != valueMap_.end()) { + return valueMap_.at(var); + } + return ReadVariableRecursive(var); +} + +AddrShift StubOPtimizerLabelImplement::ReadVariableRecursive(StubVariable *var) +{ + AddrShift val; + OpCode opcode = CircuitBuilder::GetSelectOpCodeFromMachineType(var->Type()); + if (!IsSealed()) { + // only loopheader gate will be not sealed + int valueCounts = static_cast(this->predecessors.size()) + 1; + + val = env_->GetCircuitBuilder().NewSelectorGate(opcode, predeControl_, valueCounts); + env_->AddSelectorToLabel(val, StubOptimizerLabel(this)); + incompletePhis_[var] = val; + } else if (predecessors.size() == 1) { + val = predecessors[0]->ReadVariable(var); + } else { + val = env_->GetCircuitBuilder().NewSelectorGate(opcode, predeControl_, this->predecessors.size()); + env_->AddSelectorToLabel(val, StubOptimizerLabel(this)); + WriteVariable(var, val); + val = var->AddPhiOperand(val); + } + WriteVariable(var, val); + return val; +} + +void StubOPtimizerLabelImplement::Bind() +{ + ASSERT(!predecessors.empty()); + if (IsNeedSeal()) { + Seal(); + MergeAllControl(); + } +} + +void StubOPtimizerLabelImplement::MergeAllControl() +{ + if (predecessors.size() < 2) { // 2 : Loop Head only support two predecessors + return; + } + + if (IsLoopHead()) { + ASSERT(predecessors.size() == 2); // 2 : Loop Head only support two predecessors + ASSERT(otherPredeControls_.size() == 1); + env_->GetCircuit().NewIn(predeControl_, 1, otherPredeControls_[0]); + return; + } + + // merge all control of predecessors + std::vector inGates(predecessors.size()); + size_t i = 0; + ASSERT(predeControl_ != -1); + ASSERT((otherPredeControls_.size() + 1) == predecessors.size()); + inGates[i++] = predeControl_; + for (auto in : otherPredeControls_) { + inGates[i++] = in; + } + + AddrShift merge = env_->GetCircuitBuilder().NewMerge(inGates.data(), inGates.size()); + predeControl_ = merge; + control_ = merge; +} + +void StubOPtimizerLabelImplement::AppendPredecessor(StubOptimizerLabelImplement *predecessor) +{ + if (predecessor != nullptr) { + predecessors.push_back(predecessor); + } +} + +bool StubOPtimizerLabelImplement::IsNeedSeal() const +{ + auto control = env_->GetCircuit().LoadGatePtr(predeControl_); + auto numsInList = control->GetOpCode().GetOpCodeNumInsArray(control->GetBitField()); + return predecessors.size() >= numsInList[0]; +} + +bool StubOPtimizerLabelImplement::IsLoopHead() const +{ + return env_->GetCircuit().IsLoopHead(predeControl_); +} + +Environment::Environment(const char *name, size_t arguments) + : builder_(&circuit_), arguments_(arguments), method_name_(name) +{ + for (size_t i = 0; i < arguments; i++) { + arguments_[i] = builder_.NewArguments(i); + } + entry_ = StubOptimizerLabel(NewStubOptimizerLabel(this, Circuit::GetCircuitRoot(OpCode(OpCode::STATE_ENTRY)))); + currentLabel_ = &entry_; + currentLabel_->Seal(); +} + +Environment::Environment(Environment const &env) + : circuit_(env.circuit_), builder_(&circuit_), arguments_(env.arguments_), method_name_(env.method_name_) +{ + entry_ = StubOptimizerLabel(NewStubOptimizerLabel(this, Circuit::GetCircuitRoot(OpCode(OpCode::STATE_ENTRY)))); + currentLabel_ = &entry_; + currentLabel_->Seal(); +} + +Environment &Environment::operator=(const Environment &env) +{ + if (&env != this) { + this->circuit_ = env.circuit_; + this->arguments_ = env.arguments_; + this->method_name_ = env.method_name_; + + entry_ = StubOptimizerLabel(NewStubOptimizerLabel(this, Circuit::GetCircuitRoot(OpCode(OpCode::STATE_ENTRY)))); + currentLabel_ = &entry_; + currentLabel_->Seal(); + } + return *this; +} + +Environment::~Environment() +{ + for (auto label : rawlabels_) { + delete label; + } +} + +void StubOptimizer::Jump(Label *label) +{ + ASSERT(label); + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto jump = env_->GetCircuitBuilder().Goto(currentControl); + currentLabel->SetControl(jump); + label->AppendPredecessor(currentLabel); + label->MergeControl(currentLabel->GetControl()); + + env_->SetCurrentLabel(nullptr); +} + +void StubOptimizer::Branch(AddrShift condition, Label *trueLabel, Label *falseLabel) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + AddrShift ifBranch = env_->GetCircuitBuilder().Branch(currentControl, condition); + currentLabel->SetControl(ifBranch); + AddrShift ifTrue = env_->GetCircuitBuilder().NewIfTrue(ifBranch); + trueLabel->AppendPredecessor(env_->GetCurrentLabel()); + trueLabel->MergeControl(ifTrue); + AddrShift ifFalse = env_->GetCircuitBuilder().NewIfFalse(ifBranch); + falseLabel->AppendPredecessor(env_->GetCurrentLabel()); + falseLabel->MergeControl(ifFalse); + env_->SetCurrentLabel(nullptr); +} + +void StubOptimizer::Switch(AddrShift index, Label *defaultLabel, int32_t *keysValue, Label *keysLabel, int numberOfKeys) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + AddrShift switchBranch = env_->GetCircuitBuilder().SwitchBranch(currentControl, index, numberOfKeys); + currentLabel->SetControl(switchBranch); + for (int i = 0; i < numberOfKeys; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + AddrShift switchCase = env_->GetCircuitBuilder().NewSwitchCase(switchBranch, keysValue[i]); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + keysLabel[i].AppendPredecessor(currentLabel); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + keysLabel[i].MergeControl(switchCase); + } + + AddrShift defaultCase = env_->GetCircuitBuilder().NewDefaultCase(switchBranch); + defaultLabel->AppendPredecessor(currentLabel); + defaultLabel->MergeControl(defaultCase); + env_->SetCurrentLabel(nullptr); +} + +void StubOptimizer::LoopBegin(Label *loopHead) +{ + ASSERT(loopHead); + auto loopControl = env_->GetCircuitBuilder().LoopBegin(loopHead->GetControl()); + loopHead->SetControl(loopControl); + loopHead->SetPreControl(loopControl); + loopHead->Bind(); + env_->SetCurrentLabel(loopHead); +} + +void StubOptimizer::LoopEnd(Label *loopHead) +{ + ASSERT(loopHead); + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto loopend = env_->GetCircuitBuilder().LoopEnd(currentControl); + currentLabel->SetControl(loopend); + loopHead->AppendPredecessor(currentLabel); + loopHead->MergeControl(loopend); + loopHead->Seal(); + loopHead->MergeAllControl(); + env_->SetCurrentLabel(nullptr); +} + +AddrShift StubOptimizer::FixLoadType(AddrShift x) +{ + if (PtrValueCode() == ValueCode::INT64) { + return SExtInt32ToInt64(x); + } + if (PtrValueCode() == ValueCode::INT32) { + return TruncInt64ToInt32(x); + } + UNREACHABLE(); +} + +AddrShift StubOptimizer::LoadFromObject(MachineType type, AddrShift object, AddrShift offset) +{ + AddrShift elementsOffset = GetInteger32Constant(panda::ecmascript::JSObject::ELEMENTS_OFFSET); + if (PtrValueCode() == ValueCode::INT64) { + elementsOffset = SExtInt32ToInt64(elementsOffset); + } + // load elements in object + AddrShift elements = Load(MachineType::UINT64_TYPE, object, elementsOffset); + // load index in tagged array + AddrShift dataOffset = + Int32Add(GetInteger32Constant(panda::coretypes::Array::GetDataOffset()), + Int32Mul(offset, GetInteger32Constant(panda::ecmascript::JSTaggedValue::TaggedTypeSize()))); + if (PtrValueCode() == ValueCode::INT64) { + dataOffset = SExtInt32ToInt64(dataOffset); + } + return Load(type, ChangeInt64ToPointer(elements), dataOffset); +} + +AddrShift StubOptimizer::FindElementFromNumberDictionary(AddrShift elements, AddrShift key, Label *next) +{ + auto env = GetEnvironment(); + DEFVARIABLE(result, INT32_TYPE, GetInteger32Constant(-1)); + AddrShift capcityoffset = PtrMul(GetPtrConstant(panda::ecmascript::JSTaggedValue::TaggedTypeSize()), + GetPtrConstant(panda::ecmascript::TaggedHashTable::SIZE_INDEX)); + AddrShift dataoffset = GetPtrConstant(panda::coretypes::Array::GetDataOffset()); + AddrShift capacity = TaggedCastToInt32(Load(TAGGED_TYPE, elements, PtrAdd(dataoffset, capcityoffset))); + DEFVARIABLE(count, INT32_TYPE, GetInteger32Constant(1)); + + AddrShift pKey = Alloca(static_cast(MachineRep::K_WORD32)); + AddrShift keyStore = Store(INT32_TYPE, pKey, GetPtrConstant(0), TaggedCastToInt32(key)); + StubInterfaceDescriptor *getHash32Descriptor = GET_STUBDESCRIPTOR(GetHash32); + AddrShift len = GetInteger32Constant(sizeof(int) / sizeof(uint8_t)); + AddrShift hash = CallStub(getHash32Descriptor, GetWord64Constant(FAST_STUB_ID(GetHash32)), keyStore, {pKey, len}); + DEFVARIABLE(entry, INT32_TYPE, Word32And(hash, Int32Sub(capacity, GetInteger32Constant(1)))); + Label loopHead(env); + Label loopEnd(env); + Label afterLoop(env); + Jump(&loopHead); + LoopBegin(&loopHead); + Label afterGetKey(env); + AddrShift element = GetKeyFromNumberDictionary(elements, *entry, &afterGetKey); + Label isHole(env); + Label notHole(env); + Branch(TaggedIsHole(element), &isHole, ¬Hole); + Bind(&isHole); + Jump(&loopEnd); + Bind(¬Hole); + Label isUndefined(env); + Label notUndefined(env); + Branch(TaggedIsUndefined(element), &isUndefined, ¬Undefined); + Bind(&isUndefined); + result = GetInteger32Constant(-1); + Jump(next); + Bind(¬Undefined); + Label afterIsMatch(env); + Label isMatch(env); + Label notMatch(env); + Branch(IsMatchInNumberDictionary(key, element, &afterIsMatch), &isMatch, ¬Match); + Bind(&isMatch); + result = *entry; + Jump(next); + Bind(¬Match); + Jump(&loopEnd); + Bind(&loopEnd); + entry = Word32And(Int32Add(*entry, *count), Int32Sub(capacity, GetInteger32Constant(1))); + count = Int32Add(*count, GetInteger32Constant(1)); + LoopEnd(&loopHead); + Bind(next); + return *result; +} + +AddrShift StubOptimizer::IsMatchInNumberDictionary(AddrShift key, AddrShift other, Label *next) +{ + auto env = GetEnvironment(); + DEFVARIABLE(result, BOOL_TYPE, FalseConstant()); + Label isHole(env); + Label notHole(env); + Label isUndefined(env); + Label notUndefined(env); + Branch(TaggedIsHole(key), &isHole, ¬Hole); + Bind(&isHole); + Jump(next); + Bind(¬Hole); + Branch(TaggedIsUndefined(key), &isUndefined, ¬Undefined); + Bind(&isUndefined); + Jump(next); + Bind(¬Undefined); + Label keyIsInt(env); + Label keyNotInt(env); + Label otherIsInt(env); + Label otherNotInt(env); + Branch(TaggedIsInt(key), &keyIsInt, &keyNotInt); + Bind(&keyIsInt); + Branch(TaggedIsInt(other), &otherIsInt, &otherNotInt); + Bind(&otherIsInt); + result = Word32Equal(TaggedCastToInt32(key), TaggedCastToInt32(other)); + Jump(next); + Bind(&otherNotInt); + Jump(next); + Bind(&keyNotInt); + Jump(next); + Bind(next); + return *result; +} + +AddrShift StubOptimizer::GetKeyFromNumberDictionary(AddrShift elements, AddrShift entry, Label *next) +{ + auto env = GetEnvironment(); + DEFVARIABLE(result, TAGGED_TYPE, GetUndefinedConstant()); + Label ltZero(env); + Label notLtZero(env); + Label gtLength(env); + Label notGtLength(env); + AddrShift dictionaryLength = Load(INT32_TYPE, elements, GetPtrConstant(panda::coretypes::Array::GetLengthOffset())); + AddrShift arrayIndex = Int32Add( + GetInteger32Constant(panda::ecmascript::NumberDictionary::TABLE_HEADER_SIZE), + Int32Mul(entry, GetInteger32Constant(panda::ecmascript::NumberDictionary::ENTRY_SIZE))); + Branch(Int32LessThan(arrayIndex, GetInteger32Constant(0)), <Zero, ¬LtZero); + Bind(<Zero); + Jump(next); + Bind(¬LtZero); + Branch(Int32GreaterThan(arrayIndex, dictionaryLength), >Length, ¬GtLength); + Bind(>Length); + Jump(next); + Bind(¬GtLength); + result = GetValueFromTaggedArray(elements, arrayIndex); + Jump(next); + Bind(next); + return *result; +} + +void StubOptimizer::ThrowTypeAndReturn(AddrShift thread, int messageId, AddrShift val) +{ + StubInterfaceDescriptor *throwTypeError = GET_STUBDESCRIPTOR(ThrowTypeError); + AddrShift taggedId = IntBuildTagged(GetInteger32Constant(messageId)); + CallStub(throwTypeError, GetWord64Constant(FAST_STUB_ID(ThrowTypeError)), {thread, taggedId}); + Return(val); +} + +AddrShift StubOptimizer::TaggedToRepresentation(AddrShift value, Label *next) +{ + auto env = GetEnvironment(); + DEFVARIABLE(resultRep, INT64_TYPE, + GetWord64Constant(static_cast(panda::ecmascript::Representation::OBJECT))); + Label isInt(env); + Label notInt(env); + + Branch(TaggedIsInt(value), &isInt, ¬Int); + Bind(&isInt); + { + resultRep = GetWord64Constant(static_cast(panda::ecmascript::Representation::INT)); + Jump(next); + } + Bind(¬Int); + { + Label isDouble(env); + Label notDouble(env); + Branch(TaggedIsDouble(value), &isDouble, ¬Double); + Bind(&isDouble); + { + resultRep = GetWord64Constant(static_cast(panda::ecmascript::Representation::DOUBLE)); + Jump(next); + } + Bind(¬Double); + { + resultRep = GetWord64Constant(static_cast(panda::ecmascript::Representation::OBJECT)); + Jump(next); + } + } + Bind(next); + return *resultRep; +} + +AddrShift StubOptimizer::UpdateRepresention(AddrShift oldRep, AddrShift value, Label *next) +{ + auto env = GetEnvironment(); + DEFVARIABLE(resultRep, INT64_TYPE, oldRep); + Label isMixedRep(env); + Label notMiexedRep(env); + Branch(Word64Equal(oldRep, GetWord64Constant(static_cast(panda::ecmascript::Representation::MIXED))), + &isMixedRep, ¬MiexedRep); + Bind(&isMixedRep); + Jump(next); + Bind(¬MiexedRep); + { + Label newRepLabel(env); + AddrShift newRep = TaggedToRepresentation(value, &newRepLabel); + Label isNoneRep(env); + Label notNoneRep(env); + Branch(Word64Equal(oldRep, GetWord64Constant(static_cast(panda::ecmascript::Representation::NONE))), + &isNoneRep, ¬NoneRep); + Bind(&isNoneRep); + { + resultRep = newRep; + Jump(next); + } + Bind(¬NoneRep); + { + Label isEqaulNewRep(env); + Label notEqaualNewRep(env); + Branch(Word64NotEqual(oldRep, newRep), &isEqaulNewRep, ¬EqaualNewRep); + Bind(&isEqaulNewRep); + { + resultRep = newRep; + Jump(next); + } + Bind(¬EqaualNewRep); + { + Label defaultLabel(env); + Label intLabel(env); + Label doubleLabel(env); + Label numberLabel(env); + Label objectLabel(env); + // 4 : 4 means that there are 4 args in total. + std::array repCaseLabels = { + intLabel, + doubleLabel, + numberLabel, + objectLabel, + }; + // 4 : 4 means that there are 4 args in total. + std::array keyValues = { + static_cast(panda::ecmascript::Representation::INT), + static_cast(panda::ecmascript::Representation::DOUBLE), + static_cast(panda::ecmascript::Representation::NUMBER), + static_cast(panda::ecmascript::Representation::OBJECT), + }; + // 4 : 4 means that there are 4 cases in total. + Switch(oldRep, &defaultLabel, keyValues.data(), repCaseLabels.data(), 4); + Bind(&intLabel); + Jump(&numberLabel); + Bind(&doubleLabel); + Jump(&numberLabel); + Bind(&numberLabel); + { + Label isObjectNewRep(env); + Label notObjectNewRep(env); + Branch(Word64NotEqual(newRep, GetWord64Constant( + static_cast(panda::ecmascript::Representation::OBJECT))), + ¬ObjectNewRep, &isObjectNewRep); + Bind(¬ObjectNewRep); + { + resultRep = GetWord64Constant(static_cast(panda::ecmascript::Representation::NUMBER)); + Jump(next); + } + Bind(&isObjectNewRep); + { + resultRep = GetWord64Constant(static_cast(panda::ecmascript::Representation::MIXED)); + Jump(next); + } + } + Bind(&objectLabel); + { + resultRep = GetWord64Constant(static_cast(panda::ecmascript::Representation::MIXED)); + Jump(next); + } + Bind(&defaultLabel); + Jump(next); + } + } + } + Bind(next); + return *resultRep; +} + +void StubOptimizer::UpdateRepresention(AddrShift hclass, AddrShift value) +{ + auto env = GetEnvironment(); + Label updateRepLabel(env); + AddrShift newRep = UpdateRepresention(GetElementRepresentation(hclass), value, &updateRepLabel); + SetElementRepresentation(hclass, newRep); +} +} // namespace kungfu \ No newline at end of file diff --git a/ecmascript/compiler/stub_optimizer.h b/ecmascript/compiler/stub_optimizer.h new file mode 100644 index 0000000000000000000000000000000000000000..2b1a48aced0f64218b7c41da81af6054e6e4835a --- /dev/null +++ b/ecmascript/compiler/stub_optimizer.h @@ -0,0 +1,1093 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_STUBOPTIMIZER_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_STUBOPTIMIZER_H + +#include "ecmascript/accessor_data.h" +#include "ecmascript/compiler/circuit.h" +#include "ecmascript/compiler/circuit_builder.h" +#include "ecmascript/compiler/gate.h" +#include "ecmascript/compiler/stub_interface.h" +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/tagged_dictionary.h" + +namespace kungfu { +class CompilerOptions; +class Environment; +class StubOptimizerLabel; +class StubVariable; +class CallerDescriptor; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DEFVARIABLE(varname, type, val) StubVariable varname(GetEnvironment(), type, NextVariableId(), val) + +class StubOptimizerLabel { +public: + class StubOptimizerLabelImplement { + public: + StubOptimizerLabelImplement(Environment *env, AddrShift control) + : env_(env), control_(control), predeControl_(-1), isSealed_(false) + { + } + + ~StubOptimizerLabelImplement() = default; + + NO_MOVE_SEMANTIC(StubOptimizerLabelImplement); + NO_COPY_SEMANTIC(StubOptimizerLabelImplement); + using Variable = StubVariable; + void Seal(); + void WriteVariable(Variable *var, AddrShift value); + AddrShift ReadVariable(Variable *var); + void Bind(); + void MergeAllControl(); + void AppendPredecessor(StubOptimizerLabelImplement *predecessor); + std::vector GetPredecessors() const + { + return predecessors; + } + + void SetControl(AddrShift control) + { + control_ = control; + } + + void SetPreControl(AddrShift control) + { + predeControl_ = control; + } + + void MergeControl(AddrShift control) + { + if (predeControl_ == -1) { + predeControl_ = control; + control_ = predeControl_; + } else { + otherPredeControls_.push_back(control); + } + } + + AddrShift GetControl() const + { + return control_; + } + + private: + bool IsNeedSeal() const; + bool IsSealed() const + { + return isSealed_; + } + bool IsLoopHead() const; + AddrShift ReadVariableRecursive(Variable *var); + Environment *env_; + AddrShift control_; + AddrShift predeControl_; + std::vector otherPredeControls_; + bool isSealed_; + std::map valueMap_; + std::vector phi; + std::vector predecessors; + std::map incompletePhis_; + }; + explicit StubOptimizerLabel() = default; + explicit StubOptimizerLabel(Environment *env); + explicit StubOptimizerLabel(StubOptimizerLabelImplement *impl) : impl_(impl) {} + ~StubOptimizerLabel() = default; + using Variable = StubVariable; + StubOptimizerLabel(StubOptimizerLabel const &label) = default; + StubOptimizerLabel &operator=(StubOptimizerLabel const &label) = default; + StubOptimizerLabel(StubOptimizerLabel &&label) = default; + StubOptimizerLabel &operator=(StubOptimizerLabel &&label) = default; + + void Seal() + { + return impl_->Seal(); + } + + void WriteVariable(Variable *var, AddrShift value) + { + impl_->WriteVariable(var, value); + } + + AddrShift ReadVariable(Variable *var) + { + return impl_->ReadVariable(var); + } + + void Bind() + { + impl_->Bind(); + } + + void MergeAllControl() + { + impl_->MergeAllControl(); + } + + void AppendPredecessor(const StubOptimizerLabel *predecessor) + { + impl_->AppendPredecessor(predecessor->GetRawLabel()); + } + + std::vector GetPredecessors() const + { + std::vector labels; + for (auto rawlabel : impl_->GetPredecessors()) { + labels.emplace_back(StubOptimizerLabel(rawlabel)); + } + return labels; + } + + void SetControl(AddrShift control) + { + impl_->SetControl(control); + } + + void SetPreControl(AddrShift control) + { + impl_->SetPreControl(control); + } + + void MergeControl(AddrShift control) + { + impl_->MergeControl(control); + } + + AddrShift GetControl() const + { + return impl_->GetControl(); + } + +private: + friend class Environment; + StubOptimizerLabelImplement *GetRawLabel() const + { + return impl_; + } + StubOptimizerLabelImplement *impl_{nullptr}; +}; + +class Environment { +public: + using StubOptimizerLabelImplement = StubOptimizerLabel::StubOptimizerLabelImplement; + Environment(const char *name, size_t arguments); + ~Environment(); + Environment(Environment const &env); // copy constructor + Environment &operator=(Environment const &env); // copy constructor + + NO_MOVE_SEMANTIC(Environment); + StubOptimizerLabel *GetCurrentLabel() const + { + return currentLabel_; + } + void SetCurrentLabel(StubOptimizerLabel *label) + { + currentLabel_ = label; + } + CircuitBuilder &GetCircuitBuilder() + { + return builder_; + } + AddrShift GetArgument(size_t index) const + { + return arguments_.at(index); + } + Circuit &GetCircuit() + { + return circuit_; + } + + StubOptimizerLabel GetLabelFromSelector(AddrShift sel) + { + StubOptimizerLabelImplement *rawlabel = phi_to_labels[sel]; + return StubOptimizerLabel(rawlabel); + } + + void AddSelectorToLabel(AddrShift sel, StubOptimizerLabel label) + { + phi_to_labels[sel] = label.GetRawLabel(); + } + + StubOptimizerLabelImplement *NewStubOptimizerLabel(Environment *env, AddrShift control = -1) + { + auto impl = new StubOptimizerLabelImplement(env, control); + rawlabels_.push_back(impl); + return impl; + } + +private: + StubOptimizerLabel *currentLabel_{nullptr}; + Circuit circuit_; + CircuitBuilder builder_; + std::map phi_to_labels; + std::vector arguments_; + std::string method_name_; + StubOptimizerLabel entry_; + std::vector rawlabels_; +}; + +class StubVariable { +public: + StubVariable(Environment *env, MachineType type, uint32_t id, AddrShift value) : id_(id), type_(type), env_(env) + { + Bind(value); + env_->GetCurrentLabel()->WriteVariable(this, value); + } + ~StubVariable() = default; + NO_MOVE_SEMANTIC(StubVariable); + NO_COPY_SEMANTIC(StubVariable); + void Bind(AddrShift value) + { + currentValue_ = value; + } + AddrShift Value() const + { + return currentValue_; + } + MachineType Type() const + { + return type_; + } + bool IsBound() const + { + return currentValue_ != 0; + } + StubVariable &operator=(const AddrShift value) + { + env_->GetCurrentLabel()->WriteVariable(this, value); + Bind(value); + return *this; + } + + AddrShift operator*() + { + return env_->GetCurrentLabel()->ReadVariable(this); + } + + AddrShift AddPhiOperand(AddrShift val); + AddrShift AddOperandToSelector(AddrShift val, size_t idx, AddrShift in); + AddrShift TryRemoveTrivialPhi(AddrShift phi); + void RerouteOuts(const std::vector &outs, Gate *newGate); + + bool IsSelector(AddrShift gate) const + { + return env_->GetCircuit().IsSelector(gate); + } + + bool IsSelector(const Gate *gate) const + { + return gate->GetOpCode() >= OpCode::VALUE_SELECTOR_JS && gate->GetOpCode() <= OpCode::VALUE_SELECTOR_FLOAT64; + } + + uint32_t GetId() const + { + return id_; + } + +private: + uint32_t id_; + MachineType type_; + AddrShift currentValue_{0}; + Environment *env_; +}; + +class StubOptimizer { +public: + using Label = StubOptimizerLabel; + using Variable = StubVariable; + + explicit StubOptimizer(Environment *env) : env_(env) {} + virtual ~StubOptimizer() = default; + NO_MOVE_SEMANTIC(StubOptimizer); + NO_COPY_SEMANTIC(StubOptimizer); + virtual void GenerateCircuit() = 0; + + Environment *GetEnvironment() const + { + return env_; + } + // constant + AddrShift GetInteger32Constant(int32_t value) + { + return env_->GetCircuitBuilder().NewIntegerConstant(value); + }; + AddrShift GetWord64Constant(uint64_t value) + { + return env_->GetCircuitBuilder().NewInteger64Constant(value); + }; + + AddrShift GetPtrConstant(uint32_t value) + { +#ifdef PANDA_TARGET_AMD64 + return GetWord64Constant(value); +#endif +#ifdef PANDA_TARGET_X86 + return GetInteger32Constant(value); +#endif +#ifdef PANDA_TARGET_ARM64 + return GetWord64Constant(value); +#endif +#ifdef PANDA_TARGET_ARM32 + return GetInteger32Constant(value); +#endif + } + + AddrShift PtrMul(AddrShift x, AddrShift y) + { +#ifdef PANDA_TARGET_AMD64 + return Int64Mul(x, y); +#endif +#ifdef PANDA_TARGET_X86 + return Int32Mul(x, y); +#endif +#ifdef PANDA_TARGET_ARM64 + return Int64Mul(x, y); +#endif +#ifdef PANDA_TARGET_ARM32 + return Int32Mul(x, y); +#endif + } + + AddrShift TrueConstant() + { + return TruncInt32ToInt1(GetInteger32Constant(1)); + } + + AddrShift FalseConstant() + { + return TruncInt32ToInt1(GetInteger32Constant(0)); + } + + AddrShift GetBooleanConstant(bool value) + { + return env_->GetCircuitBuilder().NewBooleanConstant(value); + } + + AddrShift GetDoubleConstant(double value) + { + return env_->GetCircuitBuilder().NewDoubleConstant(value); + } + + AddrShift GetUndefinedConstant() + { + return env_->GetCircuitBuilder().UndefineConstant(); + } + + AddrShift GetHoleConstant() + { + return env_->GetCircuitBuilder().HoleConstant(); + } + // parameter + AddrShift Argument(size_t index) + { + return env_->GetArgument(index); + } + + AddrShift Int1Argument(size_t index) + { + AddrShift argument = Argument(index); + env_->GetCircuit().SetOpCode(argument, OpCode(OpCode::INT1_ARG)); + return argument; + } + + AddrShift Int32Argument(size_t index) + { + AddrShift argument = Argument(index); + env_->GetCircuit().SetOpCode(argument, OpCode(OpCode::INT32_ARG)); + return argument; + } + + AddrShift Int64Argument(size_t index) + { + AddrShift argument = Argument(index); + env_->GetCircuit().SetOpCode(argument, OpCode(OpCode::INT64_ARG)); + return argument; + } + + AddrShift PtrArgument(size_t index) + { + AddrShift argument = Argument(index); + if (PtrValueCode() == INT64) { + env_->GetCircuit().SetOpCode(argument, OpCode(OpCode::INT64_ARG)); + } else if (PtrValueCode() == INT32) { + env_->GetCircuit().SetOpCode(argument, OpCode(OpCode::INT32_ARG)); + } else { + UNREACHABLE(); + } + return argument; + } + + AddrShift Float32Argument(size_t index) + { + AddrShift argument = Argument(index); + env_->GetCircuit().SetOpCode(argument, OpCode(OpCode::FLOAT32_ARG)); + return argument; + } + + AddrShift Float64Argument(size_t index) + { + AddrShift argument = Argument(index); + env_->GetCircuit().SetOpCode(argument, OpCode(OpCode::FLOAT64_ARG)); + return argument; + } + + AddrShift Alloca(int size) + { + return env_->GetCircuitBuilder().Alloca(size); + } + + // control flow + AddrShift Return(AddrShift value) + { + auto control = env_->GetCurrentLabel()->GetControl(); + return env_->GetCircuitBuilder().Return(control, value); + } + + void Bind(Label *label) + { + label->Bind(); + env_->SetCurrentLabel(label); + } + void Jump(Label *label); + void Branch(AddrShift condition, Label *trueLabel, Label *falseLabel); + void Switch(AddrShift index, Label *defaultLabel, int32_t *keysValue, Label *keysLabel, int numberOfKeys); + void Seal(Label *label) + { + label->Seal(); + } + + void LoopBegin(Label *loopHead); + void LoopEnd(Label *loopHead); + + // call operation + AddrShift CallStub(StubInterfaceDescriptor *descriptor, AddrShift target, std::initializer_list args) + { + return env_->GetCircuitBuilder().NewCallGate(descriptor, target, args); + } + AddrShift CallStub(StubInterfaceDescriptor *descriptor, AddrShift target, AddrShift depend, + std::initializer_list args) + { + return env_->GetCircuitBuilder().NewCallGate(descriptor, target, depend, args); + } + + // memory + AddrShift Load(MachineType type, AddrShift base, AddrShift offset) + { + if (PtrValueCode() == ValueCode::INT64) { + AddrShift val = Int64Add(base, offset); + return env_->GetCircuitBuilder().NewLoadGate(type, val); + } + if (PtrValueCode() == ValueCode::INT32) { + AddrShift val = Int32Add(base, offset); + return env_->GetCircuitBuilder().NewLoadGate(type, val); + } + UNREACHABLE(); + } + + AddrShift Load(MachineType type, AddrShift base) + { + return env_->GetCircuitBuilder().NewLoadGate(type, base); + } + + AddrShift LoadFromObject(MachineType type, AddrShift object, AddrShift offset); + + AddrShift Store(MachineType type, AddrShift base, AddrShift offset, AddrShift value) + { + if (PtrValueCode() == ValueCode::INT64) { + AddrShift ptr = Int64Add(base, offset); + return env_->GetCircuitBuilder().NewStoreGate(type, ptr, value); + } + if (PtrValueCode() == ValueCode::INT32) { + AddrShift ptr = Int32Add(base, offset); + return env_->GetCircuitBuilder().NewStoreGate(type, ptr, value); + } + UNREACHABLE(); + } + + // arithmetic + AddrShift Int32Add(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT32_ADD), x, y); + } + + AddrShift Int64Add(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT64_ADD), x, y); + } + + AddrShift DoubleAdd(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::FLOAT64_ADD), x, y); + } + + AddrShift PtrAdd(AddrShift x, AddrShift y) + { +#ifdef PANDA_TARGET_AMD64 + return Int64Add(x, y); +#endif +#ifdef PANDA_TARGET_X86 + return Int32Add(x, y); +#endif +#ifdef PANDA_TARGET_ARM64 + return Int64Add(x, y); +#endif +#ifdef PANDA_TARGET_ARM32 + return Int32Add(x, y); +#endif + } + + AddrShift Int32Sub(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT32_SUB), x, y); + } + + AddrShift Int64Sub(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT64_SUB), x, y); + } + + AddrShift DoubleSub(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::FLOAT64_SUB), x, y); + } + + AddrShift Int32Mul(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT32_MUL), x, y); + } + + AddrShift Int64Mul(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT64_MUL), x, y); + } + + AddrShift DoubleMul(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::FLOAT64_MUL), x, y); + } + + AddrShift DoubleDiv(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::FLOAT64_DIV), x, y); + } + AddrShift Int32Div(AddrShift x, AddrShift y); + AddrShift Int64Div(AddrShift x, AddrShift y); + + // bit operation + AddrShift Word32Or(AddrShift x, AddrShift y); + AddrShift Word32And(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT32_AND), x, y); + } + AddrShift Word32Xor(AddrShift x, AddrShift y); + + AddrShift FixLoadType(AddrShift x); + + AddrShift Word64Or(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT64_OR), x, y); + } + + AddrShift Word64And(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT64_AND), x, y); + } + AddrShift Word64Xor(AddrShift x, AddrShift y); + AddrShift Word32Not(AddrShift x); + AddrShift Word64Not(AddrShift x) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT64_REV), x); + } + + AddrShift WordLogicOr(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_OR), x, y); + } + + AddrShift WordLogicAnd(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_AND), x, y); + } + + AddrShift WordLogicNot(AddrShift x) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_REV), x); + } + + AddrShift Word32LSL(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT32_LSL), x, y); + } + AddrShift Word64LSL(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT64_LSL), x, y); + } + AddrShift Word32LSR(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT32_LSR), x, y); + } + AddrShift Word64LSR(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT64_LSR), x, y); + } + AddrShift Word32Sar(AddrShift x, AddrShift y); + AddrShift Word64Sar(AddrShift x, AddrShift y); + AddrShift TaggedIsInt(AddrShift x) + { + return Word64Equal(Word64And(x, GetWord64Constant(panda::ecmascript::JSTaggedValue::TAG_MASK)), + GetWord64Constant(panda::ecmascript::JSTaggedValue::TAG_INT)); + } + + AddrShift TaggedIsDouble(AddrShift x) + { + return Word32Equal(WordLogicOr(SExtInt1ToInt32(TaggedIsInt(x)), SExtInt1ToInt32(TaggedIsObject(x))), + GetInteger32Constant(0)); + } + + AddrShift TaggedIsObject(AddrShift x) + { + return Word64Equal(Word64And(x, GetWord64Constant(panda::ecmascript::JSTaggedValue::TAG_MASK)), + GetWord64Constant(panda::ecmascript::JSTaggedValue::TAG_OBJECT)); + } + + AddrShift TaggedIsNumber(AddrShift x) + { + return TruncInt32ToInt1(WordLogicOr(SExtInt1ToInt32(TaggedIsInt(x)), SExtInt1ToInt32(TaggedIsDouble(x)))); + } + + AddrShift TaggedIsHole(AddrShift x) + { + return Word64Equal(x, GetWord64Constant(panda::ecmascript::JSTaggedValue::VALUE_HOLE)); + } + + AddrShift TaggedIsNotHole(AddrShift x) + { + return Word64NotEqual(x, GetWord64Constant(panda::ecmascript::JSTaggedValue::VALUE_HOLE)); + } + + AddrShift TaggedIsUndefined(AddrShift x) + { + return Word64Equal(x, GetWord64Constant(panda::ecmascript::JSTaggedValue::VALUE_UNDEFINED)); + } + + AddrShift TaggedIsSpecial(AddrShift x) + { + return TruncInt32ToInt1(WordLogicAnd( + SExtInt1ToInt32( + Word64Equal(Word64And(x, GetWord64Constant(~panda::ecmascript::JSTaggedValue::TAG_SPECIAL_MASK)), + GetWord64Constant(0))), + WordLogicOr(SExtInt1ToInt32(Word64NotEqual( + Word64And(x, GetWord64Constant(panda::ecmascript::JSTaggedValue::TAG_SPECIAL_MASK)), + GetWord64Constant(0))), + SExtInt1ToInt32(TaggedIsHole(x))))); + } + + AddrShift TaggedIsHeapObject(AddrShift x) + { + return TruncInt32ToInt1( + WordLogicAnd(SExtInt1ToInt32(TaggedIsObject(x)), WordLogicNot(SExtInt1ToInt32(TaggedIsSpecial(x))))); + } + + AddrShift DoubleIsNAN(AddrShift x) + { + AddrShift diff = DoubleEqual(x, x); + return Word32NotEqual(SExtInt1ToInt32(diff), GetInteger32Constant(0)); + } + AddrShift IntBuildTagged(AddrShift x) + { + AddrShift val = ZExtInt32ToInt64(x); + return Word64Or(val, GetWord64Constant(panda::ecmascript::JSTaggedValue::TAG_INT)); + } + AddrShift DoubleBuildTagged(AddrShift x) + { + AddrShift val = CastDoubleToInt64(x); + return Int64Add(val, GetWord64Constant(panda::ecmascript::JSTaggedValue::DOUBLE_ENCODE_OFFSET)); + } + + AddrShift CastDoubleToInt64(AddrShift x) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::FLOAT64_TO_INT64), x); + } + + // compare operation + AddrShift Word32Equal(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_EQ), x, y); + } + + AddrShift Word32NotEqual(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_NE), x, y); + } + + AddrShift Word64Equal(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT64_EQ), x, y); + } + + AddrShift DoubleEqual(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::FLOAT64_EQ), x, y); + } + AddrShift Word64NotEqual(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT64_NE), x, y); + } + + AddrShift Int32GreaterThan(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_SGT), x, y); + } + + AddrShift Int32LessThan(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_SLT), x, y); + } + + AddrShift Int32GreaterThanOrEqual(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_SGE), x, y); + } + + AddrShift Int32LessThanOrEqual(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_SLE), x, y); + } + + AddrShift Word32GreaterThan(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_UGT), x, y); + } + + AddrShift Word32LessThan(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_ULT), x, y); + } + + AddrShift Word32LessThanOrEqual(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_ULE), x, y); + } + + AddrShift Word32GreaterThanOrEqual(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT32_UGE), x, y); + } + + AddrShift Int64GreaterThan(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT64_SGT), x, y); + } + + AddrShift Int64LessThan(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT64_SLT), x, y); + } + + AddrShift Int64LessThanOrEqual(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT64_SLE), x, y); + } + + AddrShift Int64GreaterThanOrEqual(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT64_SGE), x, y); + } + + AddrShift Word64GreaterThan(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT64_UGT), x, y); + } + + AddrShift Word64LessThan(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT64_ULT), x, y); + } + + AddrShift Word64LessThanOrEqual(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT64_ULE), x, y); + } + + AddrShift Word64GreaterThanOrEqual(AddrShift x, AddrShift y) + { + return env_->GetCircuitBuilder().NewLogicGate(OpCode(OpCode::INT64_UGE), x, y); + } + + // cast operation + AddrShift ChangeInt64ToInt32(AddrShift val) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::TRUNC_INT64_TO_INT32), val); + } + + AddrShift ChangeInt64ToPointer(AddrShift val) + { + if (PtrValueCode() == ValueCode::INT32) { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::TRUNC_INT64_TO_INT32), val); + } + return val; + } + + AddrShift ChangeInt32ToPointer(AddrShift val) + { + if (PtrValueCode() == ValueCode::INT32) { + return val; + } + return ZExtInt32ToInt64(val); + } + + // math operation + AddrShift Sqrt(AddrShift x); + + AddrShift GetSetterFromAccessor(AddrShift accessor) + { + AddrShift setterOffset = GetPtrConstant(panda::ecmascript::AccessorData::SETTER_OFFSET); + return Load(MachineType::UINT64_TYPE, accessor, setterOffset); + } + + AddrShift GetElements(AddrShift object) + { + AddrShift elementsOffset = GetInteger32Constant(panda::ecmascript::JSObject::ELEMENTS_OFFSET); + if (PtrValueCode() == ValueCode::INT64) { + elementsOffset = SExtInt32ToInt64(elementsOffset); + } + // load elements in object + return Load(MachineType::UINT64_TYPE, object, elementsOffset); + } + + // object operation + AddrShift LoadHClass(AddrShift object) + { + return ChangeInt32ToPointer(Load(UINT32_TYPE, object, GetPtrConstant(panda::ObjectHeader::GetClassOffset()))); + } + + AddrShift GetObjectType(AddrShift object) + { + AddrShift hclass = LoadHClass(object); + AddrShift bitfieldOffset = GetPtrConstant(panda::ecmascript::JSHClass::BIT_FIELD_OFFSET); + + AddrShift bitfield = Load(INT64_TYPE, hclass, bitfieldOffset); + + return ChangeInt64ToInt32( + Word64And(Word64LSR(bitfield, GetWord64Constant(panda::ecmascript::JSHClass::ExtensibleBit::START_BIT)), + GetWord64Constant((1LLU << panda::ecmascript::JSHClass::ExtensibleBit::SIZE) - 1))); + } + + AddrShift IsDictionaryMode(AddrShift object) + { + return Word32NotEqual(Word32And(Load(UINT32_TYPE, LoadHClass(object), GetPtrConstant(0)), + GetInteger32Constant(panda::HClass::IS_DICTIONARY_ARRAY)), + GetInteger32Constant(0)); + } + + AddrShift IsExtensible(AddrShift object) + { + AddrShift hclass = LoadHClass(object); + AddrShift bitfieldOffset = GetPtrConstant(panda::ecmascript::JSHClass::BIT_FIELD_OFFSET); + + AddrShift bitfield = Load(INT64_TYPE, hclass, bitfieldOffset); + // decode + return Word64NotEqual( + Word64And(Word64LSR(bitfield, GetWord64Constant(panda::ecmascript::JSHClass::ExtensibleBit::START_BIT)), + GetWord64Constant((1LLU << panda::ecmascript::JSHClass::ExtensibleBit::SIZE) - 1)), + GetWord64Constant(0)); + } + + AddrShift IsJsProxy(AddrShift obj) + { + AddrShift objectType = GetObjectType(obj); + return Word32Equal(objectType, GetInteger32Constant(static_cast(panda::ecmascript::JSType::JS_PROXY))); + } + + AddrShift IsWritable(AddrShift attr) + { + return Word32NotEqual( + Word32And( + Word32LSR(attr, GetInteger32Constant(panda::ecmascript::PropertyAttributes::WritableField::START_BIT)), + GetInteger32Constant((1LLU << panda::ecmascript::PropertyAttributes::WritableField::SIZE) - 1)), + GetInteger32Constant(0)); + } + + AddrShift IsAcesscor(AddrShift attr) + { + return Word32NotEqual( + Word32And(Word32LSR(attr, GetInteger32Constant( + panda::ecmascript::PropertyAttributes::IsAccessorField::START_BIT)), + GetInteger32Constant((1LLU << panda::ecmascript::PropertyAttributes::IsAccessorField::SIZE) - 1)), + GetInteger32Constant(0)); + } + + AddrShift GetPrototypeFromHClass(AddrShift hclass) + { + AddrShift protoOffset = GetPtrConstant(panda::ecmascript::JSHClass::PROTOTYPE_OFFSET); + return Load(TAGGED_TYPE, hclass, protoOffset); + } + + void StoreElement(AddrShift elements, AddrShift index, AddrShift value) + { + AddrShift offset = + PtrMul(ChangeInt32ToPointer(index), GetPtrConstant(panda::ecmascript::JSTaggedValue::TaggedTypeSize())); + AddrShift dataOffset = PtrAdd(offset, GetPtrConstant(panda::coretypes::Array::GetDataOffset())); + Store(TAGGED_TYPE, elements, dataOffset, value); + } + + void ThrowTypeAndReturn(AddrShift thread, int messageId, AddrShift val); + + AddrShift GetValueFromTaggedArray(AddrShift elements, AddrShift index) + { + AddrShift offset = + PtrMul(ChangeInt32ToPointer(index), GetPtrConstant(panda::ecmascript::JSTaggedValue::TaggedTypeSize())); + AddrShift dataOffset = PtrAdd(offset, GetPtrConstant(panda::coretypes::Array::GetDataOffset())); + return Load(TAGGED_TYPE, elements, dataOffset); + } + + AddrShift TaggedToRepresentation(AddrShift value, Label *next); + + AddrShift GetElementRepresentation(AddrShift hclass) + { + AddrShift bitfieldOffset = GetPtrConstant(panda::ecmascript::JSHClass::BIT_FIELD_OFFSET); + AddrShift bitfield = Load(INT64_TYPE, hclass, bitfieldOffset); + return Word64And( + Word64LSR(bitfield, GetWord64Constant(panda::ecmascript::JSHClass::ElementRepresentationBits::START_BIT)), + GetWord64Constant(((1LLU << panda::ecmascript::JSHClass::ElementRepresentationBits::SIZE) - 1))); + } + + void SetElementRepresentation(AddrShift hclass, AddrShift value) + { + AddrShift bitfieldOffset = GetPtrConstant(panda::ecmascript::JSHClass::BIT_FIELD_OFFSET); + AddrShift oldValue = Load(INT64_TYPE, hclass, bitfieldOffset); + Store(INT64_TYPE, hclass, bitfieldOffset, + Word64Or(Word64And(oldValue, + GetWord64Constant(~panda::ecmascript::JSHClass::ElementRepresentationBits::Mask())), + Word64LSR(value, GetWord64Constant( + panda::ecmascript::JSHClass::ElementRepresentationBits::START_BIT)))); + } + + void UpdateValueAndDetails(AddrShift elements, AddrShift index, AddrShift value, AddrShift attr) + { + AddrShift arrayIndex = Int32Add(GetInteger32Constant(panda::ecmascript::NameDictionary::TABLE_HEADER_SIZE), + Int32Mul(index, GetInteger32Constant(panda::ecmascript::NameDictionary::ENTRY_SIZE))); + AddrShift valueIndex = Int32Add(arrayIndex, + GetInteger32Constant(panda::ecmascript::NameDictionary::ENTRY_VALUE_INDEX)); + AddrShift attributesIndex = Int32Add(arrayIndex, + GetInteger32Constant(panda::ecmascript::NameDictionary::ENTRY_DETAILS_INDEX)); + StoreElement(elements, valueIndex, value); + StoreElement(elements, attributesIndex, IntBuildTagged(attr)); + } + + void UpdateRepresention(AddrShift hclass, AddrShift value); + + AddrShift UpdateRepresention(AddrShift oldRep, AddrShift value, Label *next); + + AddrShift GetDetailsFromDictionary(AddrShift elements, AddrShift entry) + { + AddrShift arrayIndex = Int32Add( + GetInteger32Constant(panda::ecmascript::NumberDictionary::TABLE_HEADER_SIZE), + Int32Mul(entry, GetInteger32Constant(panda::ecmascript::NumberDictionary::ENTRY_SIZE))); + AddrShift attributesIndex = Int32Add(arrayIndex, + GetInteger32Constant(panda::ecmascript::NameDictionary::ENTRY_DETAILS_INDEX)); + return GetValueFromTaggedArray(elements, attributesIndex); + } + + AddrShift GetValueFromDictionary(AddrShift elements, AddrShift entry) + { + AddrShift arrayIndex = Int32Add( + GetInteger32Constant(panda::ecmascript::NumberDictionary::TABLE_HEADER_SIZE), + Int32Mul(entry, GetInteger32Constant(panda::ecmascript::NumberDictionary::ENTRY_SIZE))); + AddrShift valueIndex = Int32Add(arrayIndex, + GetInteger32Constant(panda::ecmascript::NameDictionary::ENTRY_VALUE_INDEX)); + return GetValueFromTaggedArray(elements, valueIndex); + } + + AddrShift GetKeyFromNumberDictionary(AddrShift elements, AddrShift entry, Label *next); + + AddrShift IsMatchInNumberDictionary(AddrShift key, AddrShift other, Label *next); + + AddrShift FindElementFromNumberDictionary(AddrShift elements, AddrShift key, Label *next); + + AddrShift TaggedCastToInt32(AddrShift x) + { + AddrShift val = Word64And(x, GetWord64Constant(~panda::ecmascript::JSTaggedValue::TAG_MASK)); + return ChangeInt64ToInt32(val); + } + + AddrShift TaggedCastToDouble(AddrShift x) + { + AddrShift val = Int64Sub(x, GetWord64Constant(panda::ecmascript::JSTaggedValue::DOUBLE_ENCODE_OFFSET)); + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT64_TO_FLOAT64), val); + } + + AddrShift CastInt32ToFloat64(AddrShift x) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT32_TO_FLOAT64), x); + } + + AddrShift SExtInt32ToInt64(AddrShift x) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::SEXT_INT32_TO_INT64), x); + } + + AddrShift SExtInt1ToInt64(AddrShift x) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::SEXT_INT1_TO_INT64), x); + } + + AddrShift SExtInt1ToInt32(AddrShift x) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::SEXT_INT1_TO_INT32), x); + } + + AddrShift ZExtInt32ToInt64(AddrShift x) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::ZEXT_INT32_TO_INT64), x); + } + + AddrShift ZExtInt1ToInt64(AddrShift x) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::ZEXT_INT1_TO_INT64), x); + } + + AddrShift ZExtInt1ToInt32(AddrShift x) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::ZEXT_INT1_TO_INT32), x); + } + + AddrShift TruncInt64ToInt32(AddrShift x) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::TRUNC_INT64_TO_INT32), x); + } + + AddrShift TruncInt64ToInt1(AddrShift x) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::TRUNC_INT64_TO_INT1), x); + } + + AddrShift TruncInt32ToInt1(AddrShift x) + { + return env_->GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::TRUNC_INT32_TO_INT1), x); + } + + uint32_t NextVariableId() + { + return varibial_id_++; + } + +private: + Environment *env_; + uint32_t varibial_id_{0}; +}; +} // namespace kungfu +#endif // PANDA_RUNTIME_ECMASCRIPT_COMPILER_STUBOPTIMIZER_H diff --git a/ecmascript/compiler/tests/BUILD.gn b/ecmascript/compiler/tests/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..2fa8e0a953f9f651f8f7fc28ec2f8f962fe41adc --- /dev/null +++ b/ecmascript/compiler/tests/BUILD.gn @@ -0,0 +1,55 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//ark/js_runtime/test/test_helper.gni") +import("//build/test.gni") + +config("include_llvmC") { + include_dirs = [ + "//third_party/llvm-project/llvm/include/", + "//third_party/llvm-project/build/include", + "//third_party/llvm-project/llvm/build/include", + ] +} + +module_output_path = "ark/js_runtime" + +host_unittest_action("StubOptimizerTest") { + module_out_path = module_output_path + + sources = [ + # test file + "stub_optimizer_tests.cpp", + ] + configs = [ + ":include_llvm", + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_config", + "//ark/js_runtime:ark_jsruntime_public_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + "//ark/js_runtime/ecmascript/compiler:libark_jsoptimizer(${host_toolchain})", + sdk_libc_secshared_dep, + ] +} + +group("host_unittest") { + testonly = true + + # deps file + deps = [ ":StubOptimizerTestAction(${host_toolchain})" ] +} diff --git a/ecmascript/compiler/tests/stub_optimizer_tests.cpp b/ecmascript/compiler/tests/stub_optimizer_tests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..18d6db4cd51a03ee5a78c820169e1eb24311b7cf --- /dev/null +++ b/ecmascript/compiler/tests/stub_optimizer_tests.cpp @@ -0,0 +1,1330 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "gtest/gtest.h" +#include "ecmascript/compiler/fastpath_optimizer.h" +#include "ecmascript/compiler/llvm_ir_builder.h" +#include "ecmascript/compiler/llvm_mcjit_compiler.h" +#include "ecmascript/compiler/scheduler.h" +#include "ecmascript/compiler/stub_interface.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/js_array.h" +#include "ecmascript/message_string.h" +#include "ecmascript/tests/test_helper.h" + +#include "llvm/IR/Instructions.h" +#include "llvm/Support/Host.h" + +namespace panda::test { +using namespace panda::coretypes; +using namespace panda::ecmascript; +using namespace kungfu; + +class StubOptimizerTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(StubOptimizerTest, FastLoadElement) +{ + auto *factory = JSThread::Cast(thread)->GetEcmaVM()->GetFactory(); + LLVMModuleRef module = LLVMModuleCreateWithName("simple_module"); + LLVMSetTarget(module, "x86_64-unknown-linux-gnu"); + LLVMTypeRef paramTys[] = { + LLVMPointerType(LLVMInt64Type(), 0), + LLVMInt32Type(), + }; + LLVMValueRef function = + // 2 : parameter number + LLVMAddFunction(module, "FastLoadElement", LLVMFunctionType(LLVMInt64Type(), paramTys, 2, 0)); + LLVMDumpModule(module); + Environment fastEnv("FastLoadElement", 2); // 2 : parameter count + FastArrayLoadElementOptimizer optimizer(&fastEnv); + optimizer.GenerateCircuit(); + auto netOfGates = fastEnv.GetCircuit(); + netOfGates.PrintAllGates(); + auto cfg = Scheduler::Run(&netOfGates); + for (size_t bbIdx = 0; bbIdx < cfg.size(); bbIdx++) { + std::cout << (netOfGates.GetOpCode(cfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdx = cfg[bbIdx].size(); instIdx > 0; instIdx--) { + netOfGates.Print(cfg[bbIdx][instIdx - 1]); + } + } + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, function); + llvmBuilder.Build(); + LLVMMCJITCompiler compiler(module); + compiler.Run(); + auto engine = compiler.GetEngine(); + /* exec function */ + auto fn = reinterpret_cast(LLVMGetPointerToGlobal(engine, function)); + // 5 : 5 means that there are 5 cases in total. + JSHandle values(factory->NewTaggedArray(5)); + // 5 : 5 means that there are 5 cases in total. + for (int i = 0; i < 5; i++) { + values->Set(thread, i, JSTaggedValue(i)); + } + JSHandle array(JSArray::CreateArrayFromList(thread, values)); + JSArray *arr = array.GetObject(); + auto valValid = fn(arr, 1); + EXPECT_EQ(valValid, 0xffff000000000001); + // 6 : size of array + auto valUndefine = fn(arr, 6); + EXPECT_EQ(valUndefine, 0xa); + std::cout << "valValid = " << std::hex << valValid << std::endl; + std::cout << "valUndefine = " << std::hex << valUndefine << std::endl; +} + +class StubPhiOptimizer : public StubOptimizer { +public: + explicit StubPhiOptimizer(Environment *env) : StubOptimizer(env) {} + ~StubPhiOptimizer() = default; + NO_MOVE_SEMANTIC(StubPhiOptimizer); + NO_COPY_SEMANTIC(StubPhiOptimizer); + void GenerateCircuit() override + { + auto env = GetEnvironment(); + DEFVARIABLE(z, MachineType::INT32_TYPE, GetInteger32Constant(0)); + DEFVARIABLE(x, MachineType::INT32_TYPE, Int32Argument(0)); + StubOptimizerLabel ifTrue(env); + StubOptimizerLabel ifFalse(env); + StubOptimizerLabel next(env); + + Branch(Word32Equal(*x, GetInteger32Constant(10)), &ifTrue, &ifFalse); // 10 : size of entry + Bind(&ifTrue); + z = Int32Add(*x, GetInteger32Constant(10)); // 10 : size of entry + Jump(&next); + Bind(&ifFalse); // else + z = Int32Add(*x, GetInteger32Constant(100)); // 100 : size of entry + Jump(&next); + Bind(&next); + Return(*z); + } +}; + +HWTEST_F_L0(StubOptimizerTest, PhiGateTest) +{ + std::cout << "---------------------PhiGateTest-----------------------------------------------------" << std::endl; + LLVMModuleRef module = LLVMModuleCreateWithName("simple_module"); + LLVMSetTarget(module, "x86_64-unknown-linux-gnu"); + LLVMTypeRef paramTys[] = { + LLVMInt32Type(), + }; + LLVMValueRef function = + LLVMAddFunction(module, "PhiGateTest", LLVMFunctionType(LLVMInt32Type(), paramTys, 1, 0)); + Environment env("PhiGateTest", 1); + StubPhiOptimizer optimizer(&env); + optimizer.GenerateCircuit(); + auto netOfGates = env.GetCircuit(); + netOfGates.PrintAllGates(); + auto cfg = Scheduler::Run(&netOfGates); + for (size_t bbIdx = 0; bbIdx < cfg.size(); bbIdx++) { + std::cout << (netOfGates.GetOpCode(cfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdx = cfg[bbIdx].size(); instIdx > 0; instIdx--) { + netOfGates.Print(cfg[bbIdx][instIdx - 1]); + } + } + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, function); + llvmBuilder.Build(); + LLVMMCJITCompiler compiler(module); + compiler.Run(); + auto engine = compiler.GetEngine(); + /* exec function */ + auto fn = reinterpret_cast(LLVMGetPointerToGlobal(engine, function)); + auto val = fn(3); // 3 : size of array + auto val2 = fn(0); + std::cout << "val = " << std::dec << val << std::endl; + std::cout << "val2 = " << std::dec << val2 << std::endl; + std::cout << "+++++++++++++++++++++PhiGateTest+++++++++++++++++++++++++++++++++++++++++++++++++++++" << std::endl; +} + +class StubCallPhiOptimizer : public StubOptimizer { +public: + explicit StubCallPhiOptimizer(Environment *env) + : StubOptimizer(env), phi_descriptor_(0, 1, DEFAULT_ORDER, MachineType::INT32_TYPE) + { + std::array *params = new std::array(); + (*params)[0] = MachineType::INT32_TYPE; + phi_descriptor_.SetParameters(params->data()); + } + ~StubCallPhiOptimizer() = default; + NO_MOVE_SEMANTIC(StubCallPhiOptimizer); + NO_COPY_SEMANTIC(StubCallPhiOptimizer); + void GenerateCircuit() override + { + DEFVARIABLE(x, MachineType::INT32_TYPE, Int32Argument(0)); + x = Int32Add(*x, GetInteger32Constant(1)); + AddrShift callFoo = CallStub(&phi_descriptor_, GetWord64Constant(FAST_STUB_ID(PhiTest)), {*x}); + Return(Int32Add(callFoo, GetInteger32Constant(1))); + } + +private: + StubInterfaceDescriptor phi_descriptor_; +}; + +HWTEST_F_L0(StubOptimizerTest, CallPhiGateTest) +{ + LLVMModuleRef module = static_cast(FastStubs::GetInstance().GetModule()); + LLVMValueRef function = LLVMGetNamedFunction(module, "PhiTest"); + LLVMTypeRef barParamTys[] = { + LLVMInt32Type(), + }; + LLVMValueRef barfunction = LLVMAddFunction(module, "CallPhiGateTest_bar", + LLVMFunctionType(LLVMInt32Type(), barParamTys, 1, 0)); + Environment env("PhiTest", 1); + StubPhiOptimizer optimizer(&env); + optimizer.GenerateCircuit(); + auto netOfGates = env.GetCircuit(); + netOfGates.PrintAllGates(); + auto cfg = Scheduler::Run(&netOfGates); + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, function); + llvmBuilder.Build(); + Environment barEnv("CallPhiGateTest_bar", 1); + StubCallPhiOptimizer callphiOptimizer(&barEnv); + callphiOptimizer.GenerateCircuit(); + auto callNetOfGates = barEnv.GetCircuit(); + callNetOfGates.PrintAllGates(); + auto callCfg = Scheduler::Run(&callNetOfGates); + for (size_t bbIdx = 0; bbIdx < callCfg.size(); bbIdx++) { + std::cout << (callNetOfGates.GetOpCode(callCfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdx = callCfg[bbIdx].size(); instIdx > 0; instIdx--) { + callNetOfGates.Print(callCfg[bbIdx][instIdx - 1]); + } + } + LLVMIRBuilder barllvmBuilder(&callCfg, &callNetOfGates, module, barfunction); + barllvmBuilder.Build(); + LLVMMCJITCompiler compiler(module); + compiler.Run(); + auto engine = compiler.GetEngine(); + auto *phiTestPointer = reinterpret_cast( + reinterpret_cast(LLVMGetPointerToGlobal(engine, function))); + LLVMAddGlobalMapping(engine, function, reinterpret_cast(phiTestPointer)); + auto *barPointerbar = reinterpret_cast( + reinterpret_cast(LLVMGetPointerToGlobal(engine, barfunction))); + std::cout << std::hex << "phiTestPointer:" << reinterpret_cast(phiTestPointer) << + " barPointerbar:" << reinterpret_cast(barPointerbar) << std::endl; + compiler.Disassemble(); + int result = barPointerbar(9); + EXPECT_EQ(result, 21); + result = barPointerbar(10); + EXPECT_EQ(result, 112); +} + +class LoopOptimizer : public StubOptimizer { +public: + explicit LoopOptimizer(Environment *env) : StubOptimizer(env) {} + ~LoopOptimizer() = default; + NO_MOVE_SEMANTIC(LoopOptimizer); + NO_COPY_SEMANTIC(LoopOptimizer); + void GenerateCircuit() override + { + auto env = GetEnvironment(); + DEFVARIABLE(z, MachineType::INT32_TYPE, GetInteger32Constant(0)); + DEFVARIABLE(y, MachineType::INT32_TYPE, Int32Argument(0)); + Label loopHead(env); + Label loopEnd(env); + Label afterLoop(env); + Branch(Int32LessThan(*y, GetInteger32Constant(10)), &loopHead, &afterLoop); // 10 : size of entry + LoopBegin(&loopHead); + Label ifTrue(env); + Label ifFalse(env); + Label next(env); + Branch(Word32Equal(Int32Argument(0), GetInteger32Constant(9)), &ifTrue, &ifFalse); // 9 : size of entry + Bind(&ifTrue); + z = Int32Add(*y, GetInteger32Constant(10)); // 10 : size of entry + y = Int32Add(*z, GetInteger32Constant(1)); + Jump(&next); + Bind(&ifFalse); + z = Int32Add(*y, GetInteger32Constant(100)); // 100 : size of entry + Jump(&next); + Bind(&next); + y = Int32Add(*y, GetInteger32Constant(1)); + Branch(Int32LessThan(*y, GetInteger32Constant(10)), &loopEnd, &afterLoop); // 10 : size of entry + Bind(&loopEnd); + LoopEnd(&loopHead); + Bind(&afterLoop); + Return(*z); + } +}; + +HWTEST_F_L0(StubOptimizerTest, LoopTest) +{ + LLVMModuleRef module = LLVMModuleCreateWithName("simple_module"); + LLVMSetTarget(module, "x86_64-unknown-linux-gnu"); + LLVMTypeRef paramTys[] = { + LLVMInt32Type(), + }; + LLVMValueRef function = LLVMAddFunction(module, "LoopTest", LLVMFunctionType(LLVMInt32Type(), paramTys, 1, 0)); + Environment env("loop", 1); + LoopOptimizer optimizer(&env); + optimizer.GenerateCircuit(); + auto netOfGates = env.GetCircuit(); + netOfGates.PrintAllGates(); + auto cfg = Scheduler::Run(&netOfGates); + for (size_t bbIdx = 0; bbIdx < cfg.size(); bbIdx++) { + std::cout << (netOfGates.GetOpCode(cfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t inst_idx = cfg[bbIdx].size(); inst_idx > 0; inst_idx--) { + netOfGates.Print(cfg[bbIdx][inst_idx - 1]); + } + } + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, function); + llvmBuilder.Build(); + LLVMMCJITCompiler compiler(module); + compiler.Run(); + auto engine = compiler.GetEngine(); + + /* exec function */ + auto fn = reinterpret_cast(LLVMGetPointerToGlobal(engine, function)); + auto resValid = fn(1); + auto resValid2 = fn(9); // 9 : size of array + auto resInvalid = fn(11); // 11 : size of array + std::cout << "res for loop(1) = " << std::dec << resValid << std::endl; + std::cout << "res for loop(9) = " << std::dec << resValid2 << std::endl; + std::cout << "res for loop(11) = " << std::dec << resInvalid << std::endl; +} + +class LoopOptimizer1 : public StubOptimizer { +public: + explicit LoopOptimizer1(Environment *env) : StubOptimizer(env) {} + ~LoopOptimizer1() = default; + NO_MOVE_SEMANTIC(LoopOptimizer1); + NO_COPY_SEMANTIC(LoopOptimizer1); + void GenerateCircuit() override + { + auto env = GetEnvironment(); + DEFVARIABLE(y, MachineType::INT32_TYPE, Int32Argument(0)); + DEFVARIABLE(x, MachineType::INT32_TYPE, Int32Argument(0)); + DEFVARIABLE(z, MachineType::INT32_TYPE, Int32Argument(0)); + Label loopHead(env); + Label loopEnd(env); + Label afterLoop(env); + Branch(Int32LessThan(*y, GetInteger32Constant(10)), &loopHead, &afterLoop); // 10 : size of entry + LoopBegin(&loopHead); + x = Int32Add(*z, GetInteger32Constant(3)); // 3 : size of entry + Label ifTrue(env); + Label next(env); + Branch(Word32Equal(*x, GetInteger32Constant(9)), &ifTrue, &next); // 9 : size of entry + Bind(&ifTrue); + y = Int32Add(*z, *x); + Jump(&next); + Bind(&next); + y = Int32Add(*y, GetInteger32Constant(1)); + Branch(Int32LessThan(*y, GetInteger32Constant(10)), &loopEnd, &afterLoop); // 10 : size of entry + Bind(&loopEnd); + LoopEnd(&loopHead); + Bind(&afterLoop); + z = *y; + Return(*z); + } +}; + +HWTEST_F_L0(StubOptimizerTest, LoopTest1) +{ + LLVMModuleRef module = LLVMModuleCreateWithName("simple_module"); + LLVMSetTarget(module, "x86_64-unknown-linux-gnu"); + LLVMTypeRef paramTys[] = { + LLVMInt32Type(), + }; + LLVMValueRef function = LLVMAddFunction(module, "LoopTest1", LLVMFunctionType(LLVMInt32Type(), paramTys, 1, 0)); + Environment env("loop1", 1); + LoopOptimizer1 optimizer(&env); + optimizer.GenerateCircuit(); + auto netOfGates = env.GetCircuit(); + netOfGates.PrintAllGates(); + auto cfg = Scheduler::Run(&netOfGates); + for (size_t bbIdx = 0; bbIdx < cfg.size(); bbIdx++) { + std::cout << (netOfGates.GetOpCode(cfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdx = cfg[bbIdx].size(); instIdx > 0; instIdx--) + netOfGates.Print(cfg[bbIdx][instIdx - 1]); + } + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, function); + llvmBuilder.Build(); + LLVMMCJITCompiler compiler(module); + compiler.Run(); + auto engine = compiler.GetEngine(); + /* exec function */ + auto fn = reinterpret_cast(LLVMGetPointerToGlobal(engine, function)); + auto resValid = fn(1); + auto resValid2 = fn(9); // 9 : size of array + auto resInvalid = fn(11); // 11 : size of array + std::cout << "res for loop1(1) = " << std::dec << resValid << std::endl; + std::cout << "res for loop1(9) = " << std::dec << resValid2 << std::endl; + std::cout << "res for loop1(11) = " << std::dec << resInvalid << std::endl; +} + +class FastAddOptimizer : public StubOptimizer { +public: + explicit FastAddOptimizer(Environment *env) : StubOptimizer(env) {} + ~FastAddOptimizer() = default; + NO_MOVE_SEMANTIC(FastAddOptimizer); + NO_COPY_SEMANTIC(FastAddOptimizer); + void GenerateCircuit() override + { + auto env = GetEnvironment(); + AddrShift x = Int64Argument(0); + AddrShift y = Int64Argument(1); + StubOptimizerLabel ifTrue1(env); + StubOptimizerLabel ifTrue2(env); + StubOptimizerLabel ifFalse1(env); + StubOptimizerLabel ifFalse2(env); + StubOptimizerLabel ifTrue3(env); + StubOptimizerLabel ifTrue4(env); + StubOptimizerLabel ifTrue5(env); + StubOptimizerLabel ifFalse3(env); + StubOptimizerLabel ifFalse4(env); + StubOptimizerLabel ifFalse5(env); + StubOptimizerLabel next1(env); + StubOptimizerLabel next2(env); + StubOptimizerLabel next3(env); + // if x is number + Branch(TaggedIsNumber(x), &ifTrue1, &ifFalse1); + Bind(&ifTrue1); + // if y is number + Branch(TaggedIsNumber(y), &ifTrue2, &ifFalse2); + Bind(&ifTrue2); + Branch(TaggedIsInt(x), &ifTrue3, &ifFalse3); + Bind(&ifTrue3); + AddrShift intX = TaggedCastToInt32(x); + Branch(TaggedIsInt(y), &ifTrue4, &ifFalse4); + Bind(&ifTrue4); + AddrShift intY = TaggedCastToInt32(y); + intX = Int32Add(intX, intY); + Jump(&next3); + Bind(&ifFalse4); + AddrShift doubleY = TaggedCastToDouble(y); + AddrShift doubleX = CastInt32ToFloat64(intX); + Jump(&next2); + Bind(&ifFalse3); + doubleX = TaggedCastToDouble(x); + Branch(TaggedIsInt(y), &ifTrue5, &ifFalse5); + Bind(&ifTrue5); + intY = TaggedCastToInt32(y); + doubleY = CastInt32ToFloat64(intY); + Jump(&next2); + Bind(&ifFalse5); + doubleY = TaggedCastToDouble(y); + Jump(&next2); + Bind(&ifFalse2); + Jump(&next1); + Bind(&ifFalse1); + Jump(&next1); + Bind(&next1); + Return(GetHoleConstant()); + Bind(&next2); + doubleX = DoubleAdd(doubleX, doubleY); + Return(DoubleBuildTagged(doubleX)); + Bind(&next3); + Return(IntBuildTagged(intX)); + } +}; + +HWTEST_F_L0(StubOptimizerTest, FastAddTest) +{ + LLVMModuleRef module = LLVMModuleCreateWithName("fast_add_module"); + LLVMSetTarget(module, "x86_64-unknown-linux-gnu"); + LLVMTypeRef paramTys[] = { + LLVMInt64Type(), + LLVMInt64Type(), + }; + LLVMValueRef function = LLVMAddFunction(module, "FastAddTest", LLVMFunctionType(LLVMInt64Type(), paramTys, 2, 0)); + LLVMDumpModule(module); + Environment env("FastAdd", 2); // 2 : parameter count + FastAddOptimizer optimizer(&env); + optimizer.GenerateCircuit(); + auto netOfGates = env.GetCircuit(); + netOfGates.PrintAllGates(); + auto cfg = Scheduler::Run(&netOfGates); + for (size_t bbIdx = 0; bbIdx < cfg.size(); bbIdx++) { + std::cout << (netOfGates.GetOpCode(cfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdx = cfg[bbIdx].size(); instIdx > 0; instIdx--) { + netOfGates.Print(cfg[bbIdx][instIdx - 1]); + } + } + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, function); + llvmBuilder.Build(); + LLVMMCJITCompiler compiler(module); + compiler.Run(); + auto engine = compiler.GetEngine(); + /* exec function */ + auto fn = reinterpret_cast(LLVMGetPointerToGlobal(engine, function)); + auto resValid = fn(JSTaggedValue(1).GetRawData(), JSTaggedValue(1).GetRawData()); + auto resValid2 = fn(JSTaggedValue(2).GetRawData(), JSTaggedValue(2).GetRawData()); // 2 : test case + auto resInvalid = fn(JSTaggedValue(11).GetRawData(), JSTaggedValue(11).GetRawData()); // 11 : test case + std::cout << "res for FastAdd(1, 1) = " << std::dec << resValid.GetNumber() << std::endl; + std::cout << "res for FastAdd(2, 2) = " << std::dec << resValid2.GetNumber() << std::endl; + std::cout << "res for FastAdd(11, 11) = " << std::dec << resInvalid.GetNumber() << std::endl; +} + +class FastSubOptimizer : public StubOptimizer { +public: + explicit FastSubOptimizer(Environment *env) : StubOptimizer(env) {} + ~FastSubOptimizer() = default; + NO_MOVE_SEMANTIC(FastSubOptimizer); + NO_COPY_SEMANTIC(FastSubOptimizer); + void GenerateCircuit() override + { + auto env = GetEnvironment(); + AddrShift x = Int64Argument(0); + AddrShift y = Int64Argument(1); + StubOptimizerLabel ifTrue1(env); + StubOptimizerLabel ifTrue2(env); + StubOptimizerLabel ifFalse1(env); + StubOptimizerLabel ifFalse2(env); + StubOptimizerLabel ifTrue3(env); + StubOptimizerLabel ifTrue4(env); + StubOptimizerLabel ifTrue5(env); + StubOptimizerLabel ifFalse3(env); + StubOptimizerLabel ifFalse4(env); + StubOptimizerLabel ifFalse5(env); + StubOptimizerLabel next1(env); + StubOptimizerLabel next2(env); + StubOptimizerLabel next3(env); + // if x is number + Branch(TaggedIsNumber(x), &ifTrue1, &ifFalse1); + Bind(&ifTrue1); + // if y is number + Branch(TaggedIsNumber(y), &ifTrue2, &ifFalse2); + Bind(&ifTrue2); + Branch(TaggedIsInt(x), &ifTrue3, &ifFalse3); + Bind(&ifTrue3); + AddrShift intX = TaggedCastToInt32(x); + Branch(TaggedIsInt(y), &ifTrue4, &ifFalse4); + Bind(&ifTrue4); + AddrShift intY = TaggedCastToInt32(y); + intX = Int32Sub(intX, intY); + Jump(&next3); + Bind(&ifFalse4); + AddrShift doubleY = TaggedCastToDouble(y); + AddrShift doubleX = CastInt32ToFloat64(intX); + Jump(&next2); + Bind(&ifFalse3); + doubleX = TaggedCastToDouble(x); + Branch(TaggedIsInt(y), &ifTrue5, &ifFalse5); + Bind(&ifTrue5); + intY = TaggedCastToInt32(y); + doubleY = CastInt32ToFloat64(intY); + Jump(&next2); + Bind(&ifFalse5); + doubleY = TaggedCastToDouble(y); + Jump(&next2); + Bind(&ifFalse2); + Jump(&next1); + Bind(&ifFalse1); + Jump(&next1); + Bind(&next1); + Return(GetHoleConstant()); + Bind(&next2); + doubleX = DoubleSub(doubleX, doubleY); + Return(DoubleBuildTagged(doubleX)); + Bind(&next3); + Return(IntBuildTagged(intX)); + } +}; + +HWTEST_F_L0(StubOptimizerTest, FastSubTest) +{ + LLVMModuleRef module = LLVMModuleCreateWithName("fast_sub_module"); + LLVMSetTarget(module, "x86_64-unknown-linux-gnu"); + LLVMTypeRef paramTys[] = { + LLVMInt64Type(), + LLVMInt64Type(), + }; + LLVMValueRef function = LLVMAddFunction(module, "FastSubTest", LLVMFunctionType(LLVMInt64Type(), paramTys, 2, 0)); + LLVMDumpModule(module); + Environment env("FastSub", 2); // 2 : parameter + FastSubOptimizer optimizer(&env); + optimizer.GenerateCircuit(); + auto netOfGates = env.GetCircuit(); + netOfGates.PrintAllGates(); + auto cfg = Scheduler::Run(&netOfGates); + for (size_t bbIdx = 0; bbIdx < cfg.size(); bbIdx++) { + std::cout << (netOfGates.GetOpCode(cfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdx = cfg[bbIdx].size(); instIdx > 0; instIdx--) { + netOfGates.Print(cfg[bbIdx][instIdx - 1]); + } + } + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, function); + llvmBuilder.Build(); + LLVMMCJITCompiler compiler(module); + compiler.Run(); + auto engine = compiler.GetEngine(); + /* exec function */ + auto fn = reinterpret_cast(LLVMGetPointerToGlobal(engine, function)); + auto resA = fn(JSTaggedValue(2).GetRawData(), JSTaggedValue(1).GetRawData()); // 2 : test case + auto resB = fn(JSTaggedValue(7).GetRawData(), JSTaggedValue(2).GetRawData()); // 7, 2 : test cases + auto resC = fn(JSTaggedValue(11).GetRawData(), JSTaggedValue(11).GetRawData()); // 11 : test case + std::cout << "res for FastSub(2, 1) = " << std::dec << resA.GetNumber() << std::endl; + std::cout << "res for FastSub(7, 2) = " << std::dec << resB.GetNumber() << std::endl; + std::cout << "res for FastSub(11, 11) = " << std::dec << resC.GetNumber() << std::endl; +} + +class FastMulOptimizer : public StubOptimizer { +public: + explicit FastMulOptimizer(Environment *env) : StubOptimizer(env) {} + ~FastMulOptimizer() = default; + NO_MOVE_SEMANTIC(FastMulOptimizer); + NO_COPY_SEMANTIC(FastMulOptimizer); + void GenerateCircuit() override + { + auto env = GetEnvironment(); + AddrShift x = Int64Argument(0); + AddrShift y = Int64Argument(1); + StubOptimizerLabel ifTrue1(env); + StubOptimizerLabel ifTrue2(env); + StubOptimizerLabel ifFalse1(env); + StubOptimizerLabel ifFalse2(env); + StubOptimizerLabel ifTrue3(env); + StubOptimizerLabel ifTrue4(env); + StubOptimizerLabel ifTrue5(env); + StubOptimizerLabel ifFalse3(env); + StubOptimizerLabel ifFalse4(env); + StubOptimizerLabel ifFalse5(env); + StubOptimizerLabel next1(env); + StubOptimizerLabel next2(env); + StubOptimizerLabel next3(env); + // if x is number + Branch(TaggedIsNumber(x), &ifTrue1, &ifFalse1); + Bind(&ifTrue1); + // if y is number + Branch(TaggedIsNumber(y), &ifTrue2, &ifFalse2); + Bind(&ifTrue2); + Branch(TaggedIsInt(x), &ifTrue3, &ifFalse3); + Bind(&ifTrue3); + AddrShift intX = TaggedCastToInt32(x); + Branch(TaggedIsInt(y), &ifTrue4, &ifFalse4); + Bind(&ifTrue4); + AddrShift intY = TaggedCastToInt32(y); + intX = Int32Mul(intX, intY); + Jump(&next3); + Bind(&ifFalse4); + AddrShift doubleY = TaggedCastToDouble(y); + AddrShift doubleX = CastInt32ToFloat64(intX); + Jump(&next2); + Bind(&ifFalse3); + doubleX = TaggedCastToDouble(x); + Branch(TaggedIsInt(y), &ifTrue5, &ifFalse5); + Bind(&ifTrue5); + intY = TaggedCastToInt32(y); + doubleY = CastInt32ToFloat64(intY); + Jump(&next2); + Bind(&ifFalse5); + doubleY = TaggedCastToDouble(y); + Jump(&next2); + Bind(&ifFalse2); + Jump(&next1); + Bind(&ifFalse1); + Jump(&next1); + Bind(&next1); + Return(GetHoleConstant()); + Bind(&next2); + doubleX = DoubleMul(doubleX, doubleY); + Return(DoubleBuildTagged(doubleX)); + Bind(&next3); + Return(IntBuildTagged(intX)); + } +}; + +HWTEST_F_L0(StubOptimizerTest, FastMulTest) +{ + LLVMModuleRef module = LLVMModuleCreateWithName("fast_mul_module"); + LLVMSetTarget(module, "x86_64-unknown-linux-gnu"); + LLVMTypeRef paramTys[] = { + LLVMInt64Type(), + LLVMInt64Type(), + }; + LLVMValueRef function = LLVMAddFunction(module, "FastMulTest", LLVMFunctionType(LLVMInt64Type(), paramTys, 2, 0)); + LLVMDumpModule(module); + Environment env("FastMul", 2); // 2 : parameter count + FastMulOptimizer optimizer(&env); + optimizer.GenerateCircuit(); + auto netOfGates = env.GetCircuit(); + netOfGates.PrintAllGates(); + auto cfg = Scheduler::Run(&netOfGates); + for (size_t bbIdx = 0; bbIdx < cfg.size(); bbIdx++) { + std::cout << (netOfGates.GetOpCode(cfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdx = cfg[bbIdx].size(); instIdx > 0; instIdx--) { + netOfGates.Print(cfg[bbIdx][instIdx - 1]); + } + } + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, function); + llvmBuilder.Build(); + LLVMMCJITCompiler compiler(module); + compiler.Run(); + auto engine = compiler.GetEngine(); + + /* exec function */ + auto fn = reinterpret_cast(LLVMGetPointerToGlobal(engine, function)); + auto resA = fn(JSTaggedValue(-2).GetRawData(), JSTaggedValue(1).GetRawData()); // -2 : test case + auto resB = fn(JSTaggedValue(-7).GetRawData(), JSTaggedValue(-2).GetRawData()); // -7, -2 : test case + auto resC = fn(JSTaggedValue(11).GetRawData(), JSTaggedValue(11).GetRawData()); // 11 : test case + std::cout << "res for FastMul(-2, 1) = " << std::dec << resA.GetNumber() << std::endl; + std::cout << "res for FastMul(-7, -2) = " << std::dec << resB.GetNumber() << std::endl; + std::cout << "res for FastMul(11, 11) = " << std::dec << resC.GetNumber() << std::endl; +} + +class FastDivOptimizer : public StubOptimizer { +public: + explicit FastDivOptimizer(Environment *env) : StubOptimizer(env) {} + ~FastDivOptimizer() = default; + NO_MOVE_SEMANTIC(FastDivOptimizer); + NO_COPY_SEMANTIC(FastDivOptimizer); + void GenerateCircuit() override + { + auto env = GetEnvironment(); + AddrShift x = Int64Argument(0); + AddrShift y = Int64Argument(1); + StubOptimizerLabel ifTrue1(env); + StubOptimizerLabel ifTrue2(env); + StubOptimizerLabel ifFalse1(env); + StubOptimizerLabel ifFalse2(env); + StubOptimizerLabel ifTrue3(env); + StubOptimizerLabel ifTrue4(env); + StubOptimizerLabel ifTrue5(env); + StubOptimizerLabel ifTrue6(env); + StubOptimizerLabel ifFalse3(env); + StubOptimizerLabel ifFalse4(env); + StubOptimizerLabel ifFalse5(env); + StubOptimizerLabel ifFalse6(env); + StubOptimizerLabel next1(env); + StubOptimizerLabel next2(env); + StubOptimizerLabel next3(env); + StubOptimizerLabel next4(env); + Branch(TaggedIsNumber(x), &ifTrue1, &ifFalse1); + Bind(&ifTrue1); + // if right.IsNumber() + Branch(TaggedIsNumber(y), &ifTrue2, &ifFalse2); + Bind(&ifTrue2); + Branch(TaggedIsInt(x), &ifTrue3, &ifFalse3); + Bind(&ifTrue3); + AddrShift intX = TaggedCastToInt32(x); + AddrShift doubleX = CastInt32ToFloat64(intX); + Jump(&next2); + Bind(&ifFalse3); + doubleX = TaggedCastToDouble(x); + Jump(&next2); + Bind(&ifFalse2); + Jump(&next1); + Bind(&ifFalse1); + Jump(&next1); + Bind(&next1); + Return(GetHoleConstant()); + Bind(&next2); + Branch(TaggedIsInt(y), &ifTrue4, &ifFalse4); + Bind(&ifTrue4); + AddrShift intY = TaggedCastToInt32(y); + AddrShift doubleY = CastInt32ToFloat64(intY); + Jump(&next3); + Bind(&ifFalse4); + doubleY = TaggedCastToDouble(y); + Jump(&next3); + Bind(&next3); + Branch(DoubleEqual(doubleY, GetDoubleConstant(0.0)), &ifTrue5, &ifFalse5); + Bind(&ifTrue5); + // dLeft == 0.0 || std::isnan(dLeft) + Branch(TruncInt64ToInt1(Word64Or(SExtInt1ToInt64(DoubleEqual(doubleX, GetDoubleConstant(0.0))), + SExtInt32ToInt64(DoubleIsNAN(doubleX)))), &ifTrue6, &ifFalse6); + Bind(&ifTrue6); + Return(DoubleBuildTagged(GetDoubleConstant(base::NAN_VALUE))); + Bind(&ifFalse6); + Jump(&next4); + Bind(&next4); + AddrShift taggedX = CastDoubleToInt64(doubleX); + AddrShift taggedY = CastDoubleToInt64(doubleY); + taggedX = Word64And(Word64Or(taggedX, taggedY), GetWord64Constant(base::DOUBLE_SIGN_MASK)); + taggedX = Word64Or(taggedX, CastDoubleToInt64(GetDoubleConstant(base::POSITIVE_INFINITY))); + doubleX = TaggedCastToDouble(taggedX); + Return(DoubleBuildTagged(doubleX)); + Bind(&ifFalse5); + doubleX = DoubleDiv(doubleX, doubleY); + Return(DoubleBuildTagged(doubleX)); + } +}; + +HWTEST_F_L0(StubOptimizerTest, FastDivTest) +{ + LLVMModuleRef module = LLVMModuleCreateWithName("fast_div_module"); + LLVMSetTarget(module, "x86_64-unknown-linux-gnu"); + LLVMTypeRef paramTys[] = { + LLVMInt64Type(), + LLVMInt64Type(), + }; + LLVMValueRef function = LLVMAddFunction(module, "FastDiv", LLVMFunctionType(LLVMInt64Type(), paramTys, 2, 0)); + Environment env("FastDiv", 2); + FastDivOptimizer optimizer(&env); + optimizer.GenerateCircuit(); + auto netOfGates = env.GetCircuit(); + netOfGates.PrintAllGates(); + auto cfg = Scheduler::Run(&netOfGates); + for (size_t bbIdx = 0; bbIdx < cfg.size(); bbIdx++) { + std::cout << (netOfGates.GetOpCode(cfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdx = cfg[bbIdx].size(); instIdx > 0; instIdx--) { + netOfGates.Print(cfg[bbIdx][instIdx - 1]); + } + } + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, function); + llvmBuilder.Build(); + LLVMMCJITCompiler compiler(module); + compiler.Run(); + auto engine = compiler.GetEngine(); + auto fn = reinterpret_cast(LLVMGetPointerToGlobal(engine, function)); + uint64_t x1 = JSTaggedValue(50).GetRawData(); + uint64_t x2 = JSTaggedValue(25).GetRawData(); + std::cout << "x1 = " << x1 << " x2 = " << x2 << std::endl; + auto res = fn(x1, x2); + std::cout << "res for FastDiv(50, 25) = " << res.GetRawData() << std::endl; +} + +class FastFindOwnElementStub : public StubOptimizer { +public: + explicit FastFindOwnElementStub(Environment *env) : StubOptimizer(env) {} + ~FastFindOwnElementStub() = default; + NO_MOVE_SEMANTIC(FastFindOwnElementStub); + NO_COPY_SEMANTIC(FastFindOwnElementStub); + void GenerateCircuit() override + { + auto env = GetEnvironment(); + AddrShift obj = PtrArgument(0); + AddrShift index = Int32Argument(1); + + StubOptimizerLabel notDict(env); + StubOptimizerLabel lessOrEqual1(env); + StubOptimizerLabel greaterThan1(env); + StubOptimizerLabel isHole1(env); + StubOptimizerLabel isHole2(env); + StubOptimizerLabel isHole3(env); + StubOptimizerLabel notHole1(env); + StubOptimizerLabel notHole2(env); + StubOptimizerLabel notHole3(env); + StubOptimizerLabel isDict(env); + StubOptimizerLabel lessThan(env); + StubOptimizerLabel greaterOrEqual(env); + StubOptimizerLabel greaterThan2(env); + StubOptimizerLabel lessOrEqual2(env); + StubOptimizerLabel isUndef1(env); + StubOptimizerLabel isUndef2(env); + StubOptimizerLabel notUndef1(env); + StubOptimizerLabel notUndef2(env); + StubOptimizerLabel indexIsInt(env); + StubOptimizerLabel indexIsNotInt(env); + StubOptimizerLabel equal1(env); + StubOptimizerLabel equal2(env); + StubOptimizerLabel notEqual1(env); + StubOptimizerLabel notEqual2(env); + StubOptimizerLabel lessThanZero(env); + StubOptimizerLabel greaterOrEqualZero(env); + StubOptimizerLabel greaterThanLength(env); + StubOptimizerLabel elelmentIsInt(env); + StubOptimizerLabel elelmentIsNotInt(env); + StubOptimizerLabel notGreaterThanLength(env); + StubOptimizerLabel next1(env); + AddrShift elements = Load(POINTER_TYPE, obj, GetPtrConstant(JSObject::ELEMENTS_OFFSET)); + + Branch(IsDictionaryMode(elements), &isDict, ¬Dict); + Bind(¬Dict); + AddrShift arrayLength = Load(UINT32_TYPE, elements, GetPtrConstant(panda::coretypes::Array::GetLengthOffset())); + + Branch(Int32LessThanOrEqual(arrayLength, index), &lessOrEqual1, &greaterThan1); + Bind(&lessOrEqual1); + Jump(&next1); + Bind(&greaterThan1); + AddrShift offset = PtrMul(ChangeInt32ToPointer(index), GetPtrConstant(JSTaggedValue::TaggedTypeSize())); + AddrShift dataIndex = PtrAdd(offset, GetPtrConstant(panda::coretypes::Array::GetDataOffset())); + AddrShift value = Load(TAGGED_TYPE, elements, dataIndex); + + Branch(TaggedIsHole(value), &isHole1, ¬Hole1); + Bind(&isHole1); + Jump(&next1); + Bind(¬Hole1); + Return(value); + Bind(&next1); + Return(GetHoleConstant()); + // IsDictionary + Bind(&isDict); + offset = PtrMul(GetPtrConstant(JSTaggedValue::TaggedTypeSize()), + GetPtrConstant(GetInteger32Constant(panda::ecmascript::NumberDictionary::SIZE_INDEX))); + AddrShift data = Load(POINTER_TYPE, elements, GetPtrConstant(panda::coretypes::Array::GetDataOffset())); + AddrShift capacity = TaggedCastToInt32(Load(TAGGED_TYPE, data, offset)); + AddrShift count = GetInteger32Constant(1); + AddrShift taggedIndex = IntBuildTagged(index); + // 15 : size of entry + AddrShift entry = Word32And(GetInteger32Constant(15), Int32Sub(capacity, GetInteger32Constant(1))); + AddrShift dictionaryLength = + Load(INT32_TYPE, elements, GetPtrConstant(panda::coretypes::Array::GetLengthOffset())); + Label loopHead(env); + Label loopEnd(env); + Label afterLoop(env); + Jump(&loopHead); + LoopBegin(&loopHead); + AddrShift arrayIndex = + Int32Add(GetInteger32Constant(panda::ecmascript::NumberDictionary::TABLE_HEADER_SIZE), + Int32Mul(entry, GetInteger32Constant(panda::ecmascript::NumberDictionary::ENTRY_SIZE))); + + Branch(Int32LessThan(arrayIndex, GetInteger32Constant(0)), &lessThan, &greaterOrEqual); + Bind(&lessThan); + Return(GetHoleConstant()); + Bind(&greaterOrEqual); + Branch(Int32GreaterThan(arrayIndex, dictionaryLength), &greaterThan2, &lessOrEqual2); + Bind(&greaterThan2); + Return(GetHoleConstant()); + Bind(&lessOrEqual2); + offset = PtrMul(ChangeInt32ToPointer(arrayIndex), GetPtrConstant(JSTaggedValue::TaggedTypeSize())); + data = Load(POINTER_TYPE, elements, GetPtrConstant(panda::coretypes::Array::GetDataOffset())); + AddrShift element = Load(TAGGED_TYPE, data, offset); + Branch(TaggedIsHole(element), &isHole2, ¬Hole2); + Bind(&isHole2); + Jump(&loopEnd); + // if element is undefined + Bind(¬Hole2); + Branch(TaggedIsUndefined(element), &isUndef1, ¬Undef1); + Bind(&isUndef1); + Return(GetHoleConstant()); + Bind(¬Undef1); + Branch(TaggedIsHole(taggedIndex), &isHole3, ¬Hole3); + Bind(&isHole3); + Jump(&loopEnd); + Bind(¬Hole3); + Branch(TaggedIsUndefined(taggedIndex), &isUndef2, ¬Undef2); + Bind(&isUndef2); + Jump(&loopEnd); + Bind(¬Undef2); + Branch(TaggedIsInt(taggedIndex), &indexIsInt, &indexIsNotInt); + Bind(&indexIsNotInt); + Jump(&loopEnd); + Bind(&indexIsInt); + Branch(TaggedIsInt(element), &elelmentIsInt, &elelmentIsNotInt); + Bind(&elelmentIsNotInt); + Jump(&loopEnd); + Bind(&elelmentIsInt); + Branch(Word32Equal(TaggedCastToInt32(taggedIndex), TaggedCastToInt32(element)), &equal1, ¬Equal1); + Bind(¬Equal1); + Jump(&loopEnd); + Bind(&equal1); + Branch(Word32Equal(entry, GetInteger32Constant(-1)), &equal2, ¬Equal2); + Bind(&equal2); + Return(GetHoleConstant()); + Bind(¬Equal2); + entry = + Int32Add(GetInteger32Constant(panda::ecmascript::NumberDictionary::ENTRY_VALUE_INDEX), + Int32Add(GetInteger32Constant(panda::ecmascript::NumberDictionary::TABLE_HEADER_SIZE), + Int32Mul(entry, GetInteger32Constant(panda::ecmascript::NumberDictionary::ENTRY_SIZE)))); + Branch(Int32LessThan(entry, GetInteger32Constant(0)), &lessThanZero, &greaterOrEqualZero); + Bind(&lessThanZero); + Return(GetUndefinedConstant()); + Bind(&greaterOrEqualZero); + Branch(Int32GreaterThan(entry, dictionaryLength), &greaterThanLength, ¬GreaterThanLength); + Bind(&greaterThanLength); + Return(GetUndefinedConstant()); + Bind(¬GreaterThanLength); + offset = PtrMul(ChangeInt32ToPointer(arrayIndex), GetPtrConstant(JSTaggedValue::TaggedTypeSize())); + data = Load(POINTER_TYPE, elements, GetPtrConstant(panda::coretypes::Array::GetDataOffset())); + Return(Load(TAGGED_TYPE, data, offset)); + Bind(&loopEnd); + entry = Word32And(Int32Add(entry, count), Int32Sub(capacity, GetInteger32Constant(1))); + count = Int32Add(count, GetInteger32Constant(1)); + LoopEnd(&loopHead); + } +}; + +HWTEST_F_L0(StubOptimizerTest, FastFindOwnElementStub) +{ + LLVMModuleRef module = static_cast(FastStubs::GetInstance().GetModule()); + LLVMValueRef findFunction = LLVMGetNamedFunction(module, "FindOwnElement"); + Environment env("FastFindOwnElementStub", 2); + FastFindOwnElementStub optimizer(&env); + optimizer.GenerateCircuit(); + auto netOfGates = env.GetCircuit(); + netOfGates.PrintAllGates(); + auto cfg = Scheduler::Run(&netOfGates); + for (size_t bbIdx = 0; bbIdx < cfg.size(); bbIdx++) { + std::cout << (netOfGates.GetOpCode(cfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdx = cfg[bbIdx].size(); instIdx > 0; instIdx--) { + netOfGates.Print(cfg[bbIdx][instIdx - 1]); + } + } + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, findFunction); + llvmBuilder.Build(); + LLVMMCJITCompiler compiler(module); + compiler.Run(); +} +class FastGetElementStub : public StubOptimizer { +public: + explicit FastGetElementStub(Environment *env) + // 2 : parameter number + : StubOptimizer(env), findOwnElementDescriptor(0, 2, DEFAULT_ORDER, MachineType::TAGGED_TYPE) + { + // 2 : 2 means that there are 2 cases in total. + std::array *params = new std::array(); + (*params)[0] = MachineType::POINTER_TYPE; + (*params)[1] = MachineType::UINT32_TYPE; + findOwnElementDescriptor.SetParameters(params->data()); + } + ~FastGetElementStub() = default; + NO_MOVE_SEMANTIC(FastGetElementStub); + NO_COPY_SEMANTIC(FastGetElementStub); + + void GenerateCircuit() override + { + auto env = GetEnvironment(); + AddrShift receiver = Int64Argument(0); + AddrShift index = Int64Argument(1); + StubOptimizerLabel isHole(env); + StubOptimizerLabel notHole(env); + Label loopHead(env); + Label loopEnd(env); + Label afterLoop(env); + Label notHeapObj(env); + + Jump(&loopHead); + LoopBegin(&loopHead); + AddrShift objPtr = ChangeInt64ToPointer(receiver); + AddrShift callFindOwnElementVal = CallStub(&findOwnElementDescriptor, + GetWord64Constant(FAST_STUB_ID(FindOwnElement)), {objPtr, index}); + Branch(TaggedIsHole(callFindOwnElementVal), &isHole, ¬Hole); + Bind(¬Hole); + Return(callFindOwnElementVal); + Bind(&isHole); + receiver = Load(TAGGED_TYPE, LoadHClass(objPtr), GetPtrConstant(JSHClass::PROTOTYPE_OFFSET)); + Branch(TaggedIsHeapObject(receiver), &loopEnd, ¬HeapObj); + Bind(¬HeapObj); + Return(GetUndefinedConstant()); + Bind(&loopEnd); + LoopEnd(&loopHead); + } + + StubInterfaceDescriptor findOwnElementDescriptor; +}; + +HWTEST_F_L0(StubOptimizerTest, FastGetElementStub) +{ + LLVMModuleRef module = static_cast(FastStubs::GetInstance().GetModule()); + LLVMValueRef findFunction = LLVMGetNamedFunction(module, "FindOwnElement"); + Environment findFuncEnv("FastFindOwnElement_Foo", 2); + FastFindOwnElementStub findOptimizer(&findFuncEnv); + findOptimizer.GenerateCircuit(); + auto netOfGates = findFuncEnv.GetCircuit(); + auto cfg = Scheduler::Run(&netOfGates); + for (size_t bbIdx = 0; bbIdx < cfg.size(); bbIdx++) { + std::cout << (netOfGates.GetOpCode(cfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdx = cfg[bbIdx].size(); instIdx > 0; instIdx--) { + netOfGates.Print(cfg[bbIdx][instIdx - 1]); + } + } + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, findFunction); + llvmBuilder.Build(); + Environment getFuncEnv("FastGetElement", 2); + FastGetElementStub getOptimizer(&getFuncEnv); + getOptimizer.GenerateCircuit(); + auto getNetOfGates = getFuncEnv.GetCircuit(); + getNetOfGates.PrintAllGates(); + auto getCfg = Scheduler::Run(&getNetOfGates); + for (size_t bbIdx = 0; bbIdx < getCfg.size(); bbIdx++) { + std::cout << (getNetOfGates.GetOpCode(getCfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdx = getCfg[bbIdx].size(); instIdx > 0; instIdx--) { + getNetOfGates.Print(getCfg[bbIdx][instIdx - 1]); + } + } + llvmBuilder.Build(); + LLVMMCJITCompiler compiler(module); + compiler.Run(); +} + +class FastFindOwnElement2Stub : public StubOptimizer { +public: + explicit FastFindOwnElement2Stub(Environment *env) : StubOptimizer(env) {} + ~FastFindOwnElement2Stub() = default; + NO_MOVE_SEMANTIC(FastFindOwnElement2Stub); + NO_COPY_SEMANTIC(FastFindOwnElement2Stub); + void GenerateCircuit() override + { + auto env = GetEnvironment(); + AddrShift elements = PtrArgument(0); + AddrShift index = Int32Argument(1); + AddrShift isDict = Int32Argument(2); // 2 : 3rd argument + AddrShift attr = PtrArgument(3); // 3 : 4th argument + AddrShift indexOrEntry = PtrArgument(4); // 4 : 5th argument + isDict = ZExtInt1ToInt32(isDict); + Label notDictionary(env); + Label isDictionary(env); + Label end(env); + Branch(Word32Equal(isDict, GetInteger32Constant(0)), ¬Dictionary, &isDictionary); + Bind(¬Dictionary); + { + AddrShift elementsLength = Load(UINT32_TYPE, elements, + GetPtrConstant(panda::coretypes::Array::GetLengthOffset())); + Label outOfElements(env); + Label notOutOfElements(env); + Branch(Int32LessThanOrEqual(elementsLength, index), &outOfElements, ¬OutOfElements); + Bind(&outOfElements); + { + Return(GetHoleConstant()); + } + Bind(¬OutOfElements); + { + AddrShift value = GetValueFromTaggedArray(elements, index); + Label isHole(env); + Label notHole(env); + Branch(TaggedIsHole(value), &isHole, ¬Hole); + Bind(&isHole); + Jump(&end); + Bind(¬Hole); + { + Store(UINT32_TYPE, attr, GetPtrConstant(0), + GetInteger32Constant(PropertyAttributes::GetDefaultAttributes())); + Store(UINT32_TYPE, indexOrEntry, GetPtrConstant(0), index); + Return(value); + } + } + } + Bind(&isDictionary); + { + Label afterFindElement(env); + AddrShift entry = FindElementFromNumberDictionary(elements, IntBuildTagged(index), &afterFindElement); + Label notNegtiveOne(env); + Label negtiveOne(env); + Branch(Word32NotEqual(entry, GetInteger32Constant(-1)), ¬NegtiveOne, &negtiveOne); + Bind(¬NegtiveOne); + { + Store(UINT32_TYPE, attr, GetPtrConstant(0), GetDetailsFromDictionary(elements, entry)); + Store(UINT32_TYPE, indexOrEntry, GetPtrConstant(0), entry); + Return(GetValueFromDictionary(elements, entry)); + } + Bind(&negtiveOne); + Jump(&end); + } + Bind(&end); + Return(GetHoleConstant()); + } +}; + +class SetElementStub : public StubOptimizer { +public: + explicit SetElementStub(Environment *env) : StubOptimizer(env) {} + ~SetElementStub() = default; + NO_MOVE_SEMANTIC(SetElementStub); + NO_COPY_SEMANTIC(SetElementStub); + void GenerateCircuit() override + { + auto env = GetEnvironment(); + AddrShift thread = PtrArgument(0); + AddrShift receiver = PtrArgument(1); + DEFVARIABLE(holder, MachineType::TAGGED_POINTER_TYPE, receiver); + AddrShift index = Int32Argument(2); // 2 : 3rd argument + AddrShift value = Int64Argument(3); // 3 : 4th argument + AddrShift mayThrow = Int32Argument(4); // 4 : 5th argument + DEFVARIABLE(onPrototype, MachineType::BOOL_TYPE, FalseConstant()); + + AddrShift pattr = Alloca(static_cast(MachineRep::K_WORD32)); + AddrShift pindexOrEntry = Alloca(static_cast(MachineRep::K_WORD32)); + Label loopHead(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + AddrShift elements = GetElements(*holder); + AddrShift isDictionary = IsDictionaryMode(elements); + StubInterfaceDescriptor *findOwnElemnt2 = GET_STUBDESCRIPTOR(FindOwnElement2); + AddrShift val = CallStub(findOwnElemnt2, GetWord64Constant(FAST_STUB_ID(FindOwnElement2)), + {elements, index, isDictionary, pattr, pindexOrEntry}); + Label notHole(env); + Label isHole(env); + Branch(TaggedIsNotHole(val), ¬Hole, &isHole); + Bind(¬Hole); + { + Label isOnProtoType(env); + Label notOnProtoType(env); + Label afterOnProtoType(env); + Branch(*onPrototype, &isOnProtoType, ¬OnProtoType); + Bind(&isOnProtoType); + Jump(&afterOnProtoType); + Bind(¬OnProtoType); + { + Label isExtensible(env); + Label notExtensible(env); + Label nextExtensible(env); + Branch(IsExtensible(receiver), &isExtensible, ¬Extensible); + Bind(&isExtensible); + Jump(&nextExtensible); + Bind(¬Extensible); + { + Label isThrow(env); + Label notThrow(env); + Branch(Word32NotEqual(mayThrow, GetInteger32Constant(0)), &isThrow, ¬Throw); + Bind(&isThrow); + ThrowTypeAndReturn(thread, GET_MESSAGE_STRING_ID(SetPropertyWhenNotExtensible), + FalseConstant()); + Bind(¬Throw); + Return(FalseConstant()); + } + Bind(&nextExtensible); + StubInterfaceDescriptor *addElementInternal = GET_STUBDESCRIPTOR(AddElementInternal); + Return(CallStub(addElementInternal, GetWord64Constant(FAST_STUB_ID(AddElementInternal)), + {thread, receiver, index, value, + GetInteger32Constant(PropertyAttributes::GetDefaultAttributes())})); + } + Bind(&afterOnProtoType); + { + AddrShift attr = Load(INT32_TYPE, pattr); + Label isAccessor(env); + Label notAccessor(env); + Branch(IsAcesscor(attr), &isAccessor, ¬Accessor); + Bind(¬Accessor); + { + Label isWritable(env); + Label notWritable(env); + Branch(IsWritable(attr), &isWritable, ¬Writable); + Bind(&isWritable); + { + AddrShift elements = GetElements(receiver); + Label isDict(env); + Label notDict(env); + AddrShift indexOrEntry = Load(INT32_TYPE, pindexOrEntry); + Branch(isDictionary, &isDict, ¬Dict); + Bind(¬Dict); + { + StoreElement(elements, indexOrEntry, value); + UpdateRepresention(LoadHClass(receiver), value); + Return(TrueConstant()); + } + Bind(&isDict); + { + UpdateValueAndDetails(elements, indexOrEntry, value, attr); + Return(TrueConstant()); + } + } + Bind(¬Writable); + { + Label isThrow(env); + Label notThrow(env); + Branch(Word32NotEqual(mayThrow, GetInteger32Constant(0)), &isThrow, ¬Throw); + Bind(&isThrow); + ThrowTypeAndReturn(thread, GET_MESSAGE_STRING_ID(SetReadOnlyProperty), FalseConstant()); + Bind(¬Throw); + Return(FalseConstant()); + } + } + Bind(&isAccessor); + { + StubInterfaceDescriptor *callsetter = GET_STUBDESCRIPTOR(CallSetter); + AddrShift setter = GetSetterFromAccessor(val); + Return(CallStub(callsetter, GetWord64Constant(FAST_STUB_ID(CallSetter)), + { thread, setter, receiver, value, TruncInt32ToInt1(mayThrow) })); + } + } + } + Bind(&isHole); + { + // holder equals to JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + holder = GetPrototypeFromHClass(LoadHClass(*holder)); + Label isHeapObj(env); + Label notHeapObj(env); + Branch(TaggedIsObject(*holder), &isHeapObj, ¬HeapObj); + Bind(¬HeapObj); + { + Label isExtensible(env); + Label notExtensible(env); + Label nextExtensible(env); + Branch(IsExtensible(receiver), &isExtensible, ¬Extensible); + Bind(&isExtensible); + Jump(&nextExtensible); + Bind(¬Extensible); + { + Label isThrow(env); + Label notThrow(env); + Branch(Word32NotEqual(mayThrow, GetInteger32Constant(0)), &isThrow, ¬Throw); + Bind(&isThrow); + ThrowTypeAndReturn(thread, GET_MESSAGE_STRING_ID(SetPropertyWhenNotExtensible), + FalseConstant()); + Bind(¬Throw); + Return(FalseConstant()); + } + Bind(&nextExtensible); + { + StubInterfaceDescriptor *addElementInternal = GET_STUBDESCRIPTOR(AddElementInternal); + Return(CallStub(addElementInternal, GetWord64Constant(FAST_STUB_ID(AddElementInternal)), + {thread, receiver, index, value, + GetInteger32Constant(PropertyAttributes::GetDefaultAttributes())})); + } + } + Bind(&isHeapObj); + { + Label isJsProxy(env); + Label notJsProxy(env); + Branch(IsJsProxy(*holder), &isJsProxy, ¬JsProxy); + Bind(&isJsProxy); + { + StubInterfaceDescriptor *setProperty = GET_STUBDESCRIPTOR(JSProxySetProperty); + Return(CallStub(setProperty, GetWord64Constant(FAST_STUB_ID(JSProxySetProperty)), + { thread, *holder, IntBuildTagged(index), value, receiver, TruncInt32ToInt1(mayThrow) })); + } + Bind(¬JsProxy); + onPrototype = TrueConstant(); + } + } + } + LoopEnd(&loopHead); + } +}; + +HWTEST_F_L0(StubOptimizerTest, FastFindOwnElement2Stub) +{ + std::cout << " ------------------------FastFindOwnElement2Stub ---------------------" << std::endl; + LLVMModuleRef module = static_cast(FastStubs::GetInstance().GetModule()); + LLVMValueRef function = LLVMGetNamedFunction(module, "FindOwnElement2"); + Environment env("FindOwnElement2", 5); + FastFindOwnElement2Stub optimizer(&env); + optimizer.GenerateCircuit(); + auto netOfGates = env.GetCircuit(); + netOfGates.PrintAllGates(); + auto cfg = Scheduler::Run(&netOfGates); + for (size_t bbIdx = 0; bbIdx < cfg.size(); bbIdx++) { + std::cout << (netOfGates.GetOpCode(cfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdx = cfg[bbIdx].size(); instIdx > 0; instIdx--) { + netOfGates.Print(cfg[bbIdx][instIdx - 1]); + } + } + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, function); + llvmBuilder.Build(); + LLVMMCJITCompiler compiler(module); + compiler.Run(); + std::cout << " ++++++++++++++++++++FastFindOwnElement2Stub ++++++++++++++++++" << std::endl; +} +} // namespace panda::test diff --git a/ecmascript/compiler/type.cpp b/ecmascript/compiler/type.cpp new file mode 100644 index 0000000000000000000000000000000000000000..99ec3196d6707a33bc659de182541a606da60b09 --- /dev/null +++ b/ecmascript/compiler/type.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/compiler/type.h" + +namespace kungfu { +Type::Type(GateType payload) : payload(payload) {} + +bool Type::IsBitset() const +{ + return (this->payload & 1U) == 1; +} + +Type::~Type() {} +}; // namespace kungfu \ No newline at end of file diff --git a/ecmascript/compiler/type.h b/ecmascript/compiler/type.h new file mode 100644 index 0000000000000000000000000000000000000000..6218a3c617ed9c32098f7cf780b1ad75dcb0a6d3 --- /dev/null +++ b/ecmascript/compiler/type.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_TYPE_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_TYPE_H + +#include + +namespace kungfu { +using GateType = uint8_t; +enum TypeCode : GateType { + // for HIR + NOTYPE, + JS_ANY, + JS_NULL, + JS_UNDEFINED, + JS_BOOLEAN, + JS_NUMBER, + JS_STRING, + JS_BIGINT, + JS_SYMBOL, + JS_OBJECT, + JS_FUNCTION, + JS_ARRAY, + JS_INT8ARRAY, + JS_INT16ARRAY, + JS_INT32ARRAY, + JS_UINT8ARRAY, + JS_UINT16ARRAY, + JS_UINT32ARRAY, + JS_FLOAT32ARRAY, + JS_FLOAT64ARRAY, + // for MIR + IS_REFERENCE, + NOT_REFERENCE, +}; + +class Type { +public: + explicit Type(GateType payload); + [[nodiscard]] bool IsBitset() const; + ~Type(); + +private: + GateType payload; +}; +} // namespace kungfu + +#endif // PANDA_RUNTIME_ECMASCRIPT_COMPILER_TYPE_H diff --git a/ecmascript/compiler/verifier.cpp b/ecmascript/compiler/verifier.cpp new file mode 100644 index 0000000000000000000000000000000000000000..526ce9bffbe2fe3a32a63d3d8e2ac2f0b4233cf8 --- /dev/null +++ b/ecmascript/compiler/verifier.cpp @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/compiler/verifier.h" + +#include + +#include "ecmascript/compiler/scheduler.h" + +namespace kungfu { +bool Verifier::RunDataIntegrityCheck(const Circuit *circuit) +{ + std::unordered_set gatesSet; + std::vector gatesList; + gatesList.push_back(0); + gatesSet.insert(0); + size_t out = sizeof(Gate); + AddrShift prevGate = 0; + while (true) { + AddrShift gate = circuit->SaveGatePtr( + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + reinterpret_cast(circuit->LoadGatePtrConst(AddrShift(out)))->GetGateConst()); + if (gate < prevGate + static_cast(sizeof(Gate)) || + gate >= static_cast(circuit->GetCircuitDataSize())) { + std::cerr << "[Verifier][Error] Circuit data is corrupted (bad next gate)" << std::endl; + std::cerr << "at: " << std::dec << gate << std::endl; + return false; + } + gatesList.push_back(gate); + gatesSet.insert(gate); + prevGate = gate; + out += Gate::GetGateSize( + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + reinterpret_cast(circuit->LoadGatePtrConst(AddrShift(out)))->GetIndex() + 1); + if (out == circuit->GetCircuitDataSize()) { + break; + } + if (out > circuit->GetCircuitDataSize() || out < 0) { + std::cerr << "[Verifier][Error] Circuit data is corrupted (out of bound access)" << std::endl; + std::cerr << "at: " << std::dec << out << std::endl; + return false; + } + } + for (const auto &gate : gatesList) { + for (size_t idx = 0; idx < circuit->LoadGatePtrConst(gate)->GetNumIns(); idx++) { + const In *curIn = circuit->LoadGatePtrConst(gate)->GetInConst(idx); + if (!(circuit->GetSpaceDataStartPtrConst() < curIn && curIn < circuit->GetSpaceDataEndPtrConst())) { + std::cerr << "[Verifier][Error] Circuit data is corrupted (corrupted in list)" << std::endl; + std::cerr << "id: " << std::dec << circuit->GetId(gate) << std::endl; + return false; + } + if (gatesSet.count(circuit->SaveGatePtr(curIn->GetGateConst())) == 0) { + std::cerr << "[Verifier][Error] Circuit data is corrupted (invalid in address)" << std::endl; + std::cerr << "id: " << std::dec << circuit->GetId(gate) << std::endl; + return false; + } + } + { + const Gate *curGate = circuit->LoadGatePtrConst(gate); + if (!curGate->IsFirstOutNull()) { + const Out *curOut = curGate->GetFirstOutConst(); + if (!(circuit->GetSpaceDataStartPtrConst() < curOut && curOut < circuit->GetSpaceDataEndPtrConst())) { + std::cerr << "[Verifier][Error] Circuit data is corrupted (corrupted out list)" << std::endl; + std::cerr << "id: " << std::dec << circuit->GetId(gate) << std::endl; + return false; + } + if (gatesSet.count(circuit->SaveGatePtr(curOut->GetGateConst())) == 0) { + std::cerr << "[Verifier][Error] Circuit data is corrupted (invalid out address)" << std::endl; + std::cerr << "id: " << std::dec << circuit->GetId(gate) << std::endl; + return false; + } + while (!curOut->IsNextOutNull()) { + curOut = curOut->GetNextOutConst(); + if (!(circuit->GetSpaceDataStartPtrConst() < curOut && + curOut < circuit->GetSpaceDataEndPtrConst())) { + std::cerr << "[Verifier][Error] Circuit data is corrupted (corrupted out list)" << std::endl; + std::cerr << "id: " << std::dec << circuit->GetId(gate) << std::endl; + return false; + } + if (gatesSet.count(circuit->SaveGatePtr(curOut->GetGateConst())) == 0) { + std::cerr << "[Verifier][Error] Circuit data is corrupted (invalid out address)" << std::endl; + std::cerr << "id: " << std::dec << circuit->GetId(gate) << std::endl; + return false; + } + } + } + } + } + std::cerr << "[Verifier][Pass] Circuit data integrity is verified" << std::endl; + return true; +} + +bool Verifier::RunStateGatesCheck(const Circuit *circuit, const std::vector &bbGatesList) +{ + for (const auto &bbGate : bbGatesList) { + if (!circuit->Verify(bbGate)) { + return false; + } + } + std::cerr << "[Verifier][Pass] State gates input list schema is verified" << std::endl; + return true; +} + +bool Verifier::RunCFGSoundnessCheck(const Circuit *circuit, const std::vector &bbGatesList, + const std::unordered_map &bbGatesAddrToIdx) +{ + for (const auto &bbGate : bbGatesList) { + for (const auto &predGate : circuit->GetInVector(bbGate)) { + if (circuit->GetOpCode(predGate).IsState()) { + if (bbGatesAddrToIdx.count(predGate) == 0) { + std::cerr << "[Verifier][Error] CFG is not sound" << std::endl; + std::cerr << "Proof:" << std::endl; + std::cerr << "(id=" << circuit->GetId(predGate) << ") is pred of " + << "(id=" << circuit->GetId(bbGate) << ")" << std::endl; + std::cerr << "(id=" << circuit->GetId(bbGate) << ") is reachable from entry" << std::endl; + std::cerr << "(id=" << circuit->GetId(predGate) << ") is unreachable from entry" << std::endl; + return false; + } + } + } + } + std::cerr << "[Verifier][Pass] CFG is sound" << std::endl; + return true; +} + +bool Verifier::RunCFGIsDAGCheck(const Circuit *circuit) +{ + circuit->AdvanceTime(); + std::function dfs = [&](AddrShift cur) -> bool { + if (circuit->GetOpCode(cur) == OpCode::LOOP_BACK) { + return true; + } + circuit->SetMark(cur, MarkCode::VISITED); + for (const auto &succ : circuit->GetOutVector(cur)) { + if (circuit->GetOpCode(succ).IsState()) { + if (circuit->GetMark(succ) == MarkCode::VISITED) { + std::cerr << "[Verifier][Error] CFG without loop back edges is not directed acyclic graph" + << std::endl; + std::cerr << "Proof:" << std::endl; + std::cerr << "(id=" << circuit->GetId(succ) << ") is succ of " + << "(id=" << circuit->GetId(cur) << ")" << std::endl; + std::cerr << "(id=" << circuit->GetId(cur) << ") is reachable from " + << "(id=" << circuit->GetId(succ) << ") without loop back edges" << std::endl; + return false; + } + if (circuit->GetMark(succ) == MarkCode::FINISHED) { + return true; + } + if (!dfs(succ)) { + return false; + } + } + } + circuit->SetMark(cur, MarkCode::FINISHED); + return true; + }; + auto root = Circuit::GetCircuitRoot(OpCode(OpCode::STATE_ENTRY)); + if (!dfs(root)) { + return false; + } + std::cerr << "[Verifier][Pass] CFG without loop back edges is directed acyclic graph" << std::endl; + return true; +} + +bool Verifier::RunCFGReducibilityCheck(const Circuit *circuit, const std::vector &bbGatesList, + const std::unordered_map &bbGatesAddrToIdx, + const std::function &isAncestor) +{ + for (const auto &curGate : bbGatesList) { + if (circuit->GetOpCode(curGate) == OpCode::LOOP_BACK) { + for (const auto &succGate : circuit->GetOutVector(curGate)) { + if (circuit->GetOpCode(succGate).IsState()) { + bool isDom = isAncestor(bbGatesAddrToIdx.at(succGate), bbGatesAddrToIdx.at(curGate)); + if (!isDom) { + std::cerr << "[Verifier][Error] CFG is not reducible" << std::endl; + std::cerr << "Proof:" << std::endl; + std::cerr << "(id=" << circuit->GetId(succGate) << ") is loop back succ of " + << "(id=" << circuit->GetId(curGate) << ")" << std::endl; + std::cerr << "(id=" << circuit->GetId(succGate) << ") does not dominate " + << "(id=" << circuit->GetId(curGate) << ")" << std::endl; + return false; + } + } + } + } + } + std::cerr << "[Verifier][Pass] CFG is reducible" << std::endl; + return true; +} + +bool Verifier::RunFixedGatesCheck(const Circuit *circuit, const std::vector &fixedGatesList) +{ + for (const auto &fixedGate : fixedGatesList) { + if (!circuit->Verify(fixedGate)) { + return false; + } + } + std::cerr << "[Verifier][Pass] Fixed gates input list schema is verified" << std::endl; + return true; +} + +bool Verifier::RunFixedGatesRelationsCheck(const Circuit *circuit, const std::vector &fixedGatesList, + const std::unordered_map &bbGatesAddrToIdx, + const std::function &isAncestor) +{ + for (const auto &fixedGate : fixedGatesList) { + size_t cnt = 0; + for (const auto &predGate : circuit->GetInVector(fixedGate)) { + if (circuit->GetOpCode(predGate).IsFixed() && + circuit->GetOpCode(circuit->GetIn(predGate, 0)) != OpCode::LOOP_BACK) { + ASSERT(cnt > 0); + auto a = bbGatesAddrToIdx.at(circuit->GetIn(predGate, 0)); + auto b = bbGatesAddrToIdx.at(circuit->GetIn(circuit->GetIn(fixedGate, 0), cnt - 1)); + if (!isAncestor(a, b)) { + std::cerr << "[Verifier][Error] Fixed gates relationship is not consistent" << std::endl; + std::cerr << "Proof:" << std::endl; + std::cerr << "Fixed gate (id=" << predGate << ") is pred of fixed gate (id=" << fixedGate << ")" + << std::endl; + std::cerr << "BB_" << bbGatesAddrToIdx.at(circuit->GetIn(predGate, 0)) << " does not dominate BB_" + << bbGatesAddrToIdx.at(circuit->GetIn(circuit->GetIn(fixedGate, 0), cnt - 1)) + << std::endl; + return false; + } + } + cnt++; + } + } + std::cerr << "[Verifier][Pass] Fixed gates relationship is consistent" << std::endl; + return true; +} + +bool Verifier::RunFlowCyclesFind(const Circuit *circuit, std::vector *schedulableGatesListPtr, + const std::vector &bbGatesList, const std::vector &fixedGatesList) +{ + circuit->AdvanceTime(); + std::vector startGateList; + for (const auto &gate : bbGatesList) { + for (const auto &predGate : circuit->GetInVector(gate)) { + if (circuit->GetOpCode(predGate).IsSchedulable()) { + if (circuit->GetMark(predGate) == MarkCode::EMPTY) { + startGateList.push_back(predGate); + circuit->SetMark(predGate, MarkCode::VISITED); + } + } + } + } + for (const auto &gate : fixedGatesList) { + for (const auto &predGate : circuit->GetInVector(gate)) { + if (circuit->GetOpCode(predGate).IsSchedulable()) { + if (circuit->GetMark(predGate) == MarkCode::EMPTY) { + startGateList.push_back(predGate); + circuit->SetMark(predGate, MarkCode::VISITED); + } + } + } + } + circuit->AdvanceTime(); + std::vector cycleGatesList; + AddrShift meet = -1; + std::function dfs = [&](AddrShift cur) -> bool { + circuit->SetMark(cur, MarkCode::VISITED); + schedulableGatesListPtr->push_back(cur); + size_t numIns = circuit->LoadGatePtrConst(cur)->GetNumIns(); + for (size_t idx = 0; idx < numIns; idx++) { + const auto prev = circuit->GetIn(cur, idx); + if (circuit->GetOpCode(prev).IsSchedulable()) { + if (circuit->GetMark(prev) == MarkCode::VISITED) { + std::cerr << "[Verifier][Error] Found a data or depend flow cycle without passing selectors" + << std::endl; + std::cerr << "Proof:" << std::endl; + std::cerr << "(id=" << circuit->GetId(prev) << ") is prev of " + << "(id=" << circuit->GetId(cur) << ")" << std::endl; + std::cerr << "(id=" << circuit->GetId(prev) << ") is reachable from " + << "(id=" << circuit->GetId(cur) << ") without passing selectors" << std::endl; + meet = prev; + cycleGatesList.push_back(cur); + return false; + } + if (circuit->GetMark(prev) != MarkCode::FINISHED) { + if (!dfs(prev)) { + if (meet != -1) { + cycleGatesList.push_back(cur); + } + if (meet == cur) { + meet = -1; + } + return false; + } + } + } + } + circuit->SetMark(cur, MarkCode::FINISHED); + return true; + }; + for (const auto &startGate : startGateList) { + if (circuit->GetMark(startGate) == MarkCode::EMPTY) { + if (!dfs(startGate)) { + std::cerr << "Path:" << std::endl; + for (const auto &cycleGate : cycleGatesList) { + circuit->Print(cycleGate); + } + return false; + } + } + } + std::cerr << "[Verifier][Pass] Every directed data or depend flow cycles in circuit contain selectors" << std::endl; + return true; +} + +bool Verifier::RunSchedulableGatesCheck(const Circuit *circuit, const std::vector &schedulableGatesList) +{ + for (const auto &schedulableGate : schedulableGatesList) { + if (!circuit->Verify(schedulableGate)) { + return false; + } + } + std::cerr << "[Verifier][Pass] Schedulable gates input list schema is verified" << std::endl; + return true; +} + +bool Verifier::RunPrologGatesCheck(const Circuit *circuit, const std::vector &schedulableGatesList) +{ + for (const auto &schedulableGate : schedulableGatesList) { + for (const auto &predGate : circuit->GetInVector(schedulableGate)) { + if (circuit->GetOpCode(predGate).IsProlog()) { + if (!circuit->Verify(predGate)) { + return false; + } + } + } + } + std::cerr << "[Verifier][Pass] Prolog gates input list schema is verified" << std::endl; + return true; +} + +bool Verifier::RunSchedulingBoundsCheck(const Circuit *circuit, const std::vector &schedulableGatesList, + const std::unordered_map &bbGatesAddrToIdx, + const std::function &isAncestor, + const std::function &lowestCommonAncestor) +{ + // check existence of scheduling upper bound + std::unordered_map upperBound; + { + auto result = + Scheduler::CalculateSchedulingUpperBound(circuit, bbGatesAddrToIdx, isAncestor, schedulableGatesList); + if (!result.has_value()) { + return false; + } + upperBound = result.value(); + std::cerr << "[Verifier][Pass] Scheduling upper bounds of all schedulable gates exist" << std::endl; + } + // check existence of scheduling lower bound + std::unordered_map lowerBound; + { + auto result = Scheduler::CalculateSchedulingLowerBound(circuit, bbGatesAddrToIdx, lowestCommonAncestor); + lowerBound = result.value(); + std::cerr << "[Verifier][Pass] Scheduling lower bounds of all schedulable gates exist" << std::endl; + } + // check consistency of lower bound and upper bound + { + ASSERT(upperBound.size() == lowerBound.size()); + for (const auto &item : lowerBound) { + if (!isAncestor(upperBound.at(item.first), lowerBound.at(item.first))) { + std::cerr << "[Verifier][Error] Bounds of gate (id=" << item.first << ") is not consistent" + << std::endl; + std::cerr << "Proof:" << std::endl; + std::cerr << "Upper bound is BB_" << upperBound.at(item.first) << std::endl; + std::cerr << "Lower bound is BB_" << lowerBound.at(item.first) << std::endl; + } + } + std::cerr << "[Verifier][Pass] Bounds of all schedulable gates are consistent" << std::endl; + } + return true; +} + +std::vector Verifier::FindFixedGates(const Circuit *circuit, const std::vector &bbGatesList) +{ + std::vector fixedGatesList; + for (const auto &bbGate : bbGatesList) { + for (const auto &succGate : circuit->GetOutVector(bbGate)) { + if (circuit->GetOpCode(succGate).IsFixed()) { + fixedGatesList.push_back(succGate); + } + } + } + return fixedGatesList; +} + +bool Verifier::Run(const Circuit *circuit) +{ + if (!RunDataIntegrityCheck(circuit)) { + return false; + } + std::vector bbGatesList; + std::unordered_map bbGatesAddrToIdx; + std::vector immDom; + std::tie(bbGatesList, bbGatesAddrToIdx, immDom) = Scheduler::CalculateDominatorTree(circuit); + std::cerr << std::dec; + if (!RunStateGatesCheck(circuit, bbGatesList)) { + return false; + } + if (!RunCFGSoundnessCheck(circuit, bbGatesList, bbGatesAddrToIdx)) { + return false; + } + if (!RunCFGIsDAGCheck(circuit)) { + return false; + } + std::vector> sonList(bbGatesList.size()); + for (size_t idx = 1; idx < immDom.size(); idx++) { + sonList[immDom[idx]].push_back(idx); + } + const size_t sizeLog = ceil(log2(static_cast(bbGatesList.size())) + 1); + std::vector timeIn(bbGatesList.size()); + std::vector timeOut(bbGatesList.size()); + std::vector> jumpUp; + jumpUp.assign(bbGatesList.size(), std::vector(sizeLog + 1)); + { + size_t timestamp = 0; + std::function dfs = [&](size_t cur, size_t prev) { + timeIn[cur] = timestamp++; + jumpUp[cur][0] = prev; + for (size_t stepSize = 1; stepSize <= sizeLog; stepSize++) { + jumpUp[cur][stepSize] = jumpUp[jumpUp[cur][stepSize - 1]][stepSize - 1]; + } + for (const auto &succ : sonList[cur]) { + dfs(succ, cur); + } + timeOut[cur] = timestamp++; + }; + size_t root = 0; + dfs(root, root); + } + auto isAncestor = [timeIn, timeOut](size_t nodeA, size_t nodeB) -> bool { + return timeIn[nodeA] <= timeIn[nodeB] && timeOut[nodeA] >= timeOut[nodeB]; + }; + auto lowestCommonAncestor = [&](size_t nodeA, size_t nodeB) -> size_t { + if (isAncestor(nodeA, nodeB)) { + return nodeA; + } + if (isAncestor(nodeB, nodeA)) { + return nodeB; + } + for (size_t stepSize = sizeLog + 1; stepSize > 0; stepSize--) { + if (!isAncestor(jumpUp[nodeA][stepSize - 1], nodeB)) { + nodeA = jumpUp[nodeA][stepSize - 1]; + } + } + return jumpUp[nodeA][0]; + }; + if (!RunCFGReducibilityCheck(circuit, bbGatesList, bbGatesAddrToIdx, isAncestor)) { + return false; + } + std::vector fixedGatesList = FindFixedGates(circuit, bbGatesList); + if (!RunFixedGatesCheck(circuit, fixedGatesList)) { + return false; + } + if (!RunFixedGatesRelationsCheck(circuit, fixedGatesList, bbGatesAddrToIdx, isAncestor)) { + return false; + } + std::vector schedulableGatesList; + if (!RunFlowCyclesFind(circuit, &schedulableGatesList, bbGatesList, fixedGatesList)) { + return false; + } + if (!RunSchedulableGatesCheck(circuit, fixedGatesList)) { + return false; + } + if (!RunPrologGatesCheck(circuit, fixedGatesList)) { + return false; + } + if (!RunSchedulingBoundsCheck(circuit, schedulableGatesList, bbGatesAddrToIdx, isAncestor, lowestCommonAncestor)) { + return false; + } + return true; +} +} // namespace kungfu \ No newline at end of file diff --git a/ecmascript/compiler/verifier.h b/ecmascript/compiler/verifier.h new file mode 100644 index 0000000000000000000000000000000000000000..c3729164157c6377622e9c0a128275d156a3c53b --- /dev/null +++ b/ecmascript/compiler/verifier.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_COMPILER_VERIFIER_H +#define PANDA_RUNTIME_ECMASCRIPT_COMPILER_VERIFIER_H + +#include +#include +#include +#include +#include + +#include "ecmascript/compiler/circuit.h" + +namespace kungfu { +class Verifier { +public: + static bool RunDataIntegrityCheck(const Circuit *circuit); + static bool RunStateGatesCheck(const Circuit *circuit, const std::vector &bbGatesList); + static bool RunCFGSoundnessCheck(const Circuit *circuit, const std::vector &bbGatesList, + const std::unordered_map &bbGatesAddrToIdx); + static bool RunCFGIsDAGCheck(const Circuit *circuit); + static bool RunCFGReducibilityCheck(const Circuit *circuit, const std::vector &bbGatesList, + const std::unordered_map &bbGatesAddrToIdx, + const std::function &isAncestor); + static bool RunFixedGatesCheck(const Circuit *circuit, const std::vector &fixedGatesList); + static bool RunFixedGatesRelationsCheck(const Circuit *circuit, const std::vector &fixedGatesList, + const std::unordered_map &bbGatesAddrToIdx, + const std::function &isAncestor); + static bool RunFlowCyclesFind(const Circuit *circuit, std::vector *schedulableGatesListPtr, + const std::vector &bbGatesList, const std::vector &fixedGatesList); + static bool RunSchedulableGatesCheck(const Circuit *circuit, const std::vector &schedulableGatesList); + static bool RunPrologGatesCheck(const Circuit *circuit, const std::vector &schedulableGatesList); + static bool RunSchedulingBoundsCheck(const Circuit *circuit, const std::vector &schedulableGatesList, + const std::unordered_map &bbGatesAddrToIdx, + const std::function &isAncestor, + const std::function &lowestCommonAncestor); + static std::vector FindFixedGates(const Circuit *circuit, const std::vector &bbGatesList); + static bool Run(const Circuit *circuit); +}; +} // namespace kungfu + +#endif // PANDA_RUNTIME_ECMASCRIPT_COMPILER_VERIFIER_H diff --git a/ecmascript/dump.cpp b/ecmascript/dump.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef93394ac5a22bc71be131e8b84ee5324421c439 --- /dev/null +++ b/ecmascript/dump.cpp @@ -0,0 +1,2373 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "ecmascript/accessor_data.h" +#include "ecmascript/class_linker/program_object-inl.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_dictionary-inl.h" +#include "ecmascript/global_env.h" +#include "ecmascript/jobs/micro_job_queue.h" +#include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_async_function.h" +#include "ecmascript/js_dataview.h" +#include "ecmascript/js_date.h" +#include "ecmascript/js_float32_array.h" +#include "ecmascript/js_float64_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_int16_array.h" +#include "ecmascript/js_int32_array.h" +#include "ecmascript/js_int8_array.h" +#include "ecmascript/js_map.h" +#include "ecmascript/js_map_iterator.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_regexp.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/js_typed_array.h" +#include "ecmascript/js_uint16_array.h" +#include "ecmascript/js_uint32_array.h" +#include "ecmascript/js_uint8_array.h" +#include "ecmascript/js_uint8_clamped_array.h" +#include "ecmascript/js_weak_container.h" +#include "ecmascript/layout_info-inl.h" +#include "ecmascript/lexical_env.h" +#include "ecmascript/linked_hash_table-inl.h" +#include "ecmascript/mem/assert_scope-inl.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/tagged_array.h" +#include "ecmascript/tagged_dictionary.h" +#include "ecmascript/transitions_dictionary.h" + +namespace panda::ecmascript { +using MicroJobQueue = panda::ecmascript::job::MicroJobQueue; +using PendingJob = panda::ecmascript::job::PendingJob; + +static constexpr uint32_t DUMP_TYPE_OFFSET = 12; +static constexpr uint32_t DUMP_PROPERTY_OFFSET = 20; + +CString JSHClass::DumpJSType(JSType type) +{ + switch (type) { + case JSType::HCLASS: + return "JSHClass"; + case JSType::TAGGED_ARRAY: + return "TaggedArray"; + case JSType::TAGGED_DICTIONARY: + return "TaggedDictionary"; + case JSType::STRING: + return "BaseString"; + case JSType::JS_OBJECT: + return "Object"; + case JSType::JS_FUNCTION_BASE: + return "Function Base"; + case JSType::JS_FUNCTION: + return "Function"; + case JSType::JS_ERROR: + return "Error"; + case JSType::JS_EVAL_ERROR: + return "Eval Error"; + case JSType::JS_RANGE_ERROR: + return "Range Error"; + case JSType::JS_TYPE_ERROR: + return "Type Error"; + case JSType::JS_REFERENCE_ERROR: + return "Reference Error"; + case JSType::JS_URI_ERROR: + return "Uri Error"; + case JSType::JS_SYNTAX_ERROR: + return "Syntax Error"; + case JSType::JS_REG_EXP: + return "Regexp"; + case JSType::JS_SET: + return "Set"; + case JSType::JS_MAP: + return "Map"; + case JSType::JS_WEAK_SET: + return "WeakSet"; + case JSType::JS_WEAK_MAP: + return "WeakMap"; + case JSType::JS_DATE: + return "Date"; + case JSType::JS_BOUND_FUNCTION: + return "Bound Function"; + case JSType::JS_ARRAY: + return "Array"; + case JSType::JS_TYPED_ARRAY: + return "Typed Array"; + case JSType::JS_INT8_ARRAY: + return "Int8 Array"; + case JSType::JS_UINT8_ARRAY: + return "Uint8 Array"; + case JSType::JS_UINT8_CLAMPED_ARRAY: + return "Uint8 Clamped Array"; + case JSType::JS_INT16_ARRAY: + return "Int16 Array"; + case JSType::JS_UINT16_ARRAY: + return "Uint16 Array"; + case JSType::JS_INT32_ARRAY: + return "Int32 Array"; + case JSType::JS_UINT32_ARRAY: + return "Uint32 Array"; + case JSType::JS_FLOAT32_ARRAY: + return "Float32 Array"; + case JSType::JS_FLOAT64_ARRAY: + return "Float64 Array"; + case JSType::JS_ARGUMENTS: + return "Arguments"; + case JSType::JS_PROXY: + return "Proxy"; + case JSType::JS_PRIMITIVE_REF: + return "Primitive"; + case JSType::JS_DATA_VIEW: + return "DataView"; + case JSType::JS_ITERATOR: + return "Iterator"; + case JSType::JS_FORIN_ITERATOR: + return "ForinInterator"; + case JSType::JS_MAP_ITERATOR: + return "MapIterator"; + case JSType::JS_SET_ITERATOR: + return "SetIterator"; + case JSType::JS_ARRAY_ITERATOR: + return "ArrayIterator"; + case JSType::JS_STRING_ITERATOR: + return "StringIterator"; + case JSType::JS_ARRAY_BUFFER: + return "ArrayBuffer"; + case JSType::JS_PROXY_REVOC_FUNCTION: + return "ProxyRevocFunction"; + case JSType::PROMISE_REACTIONS: + return "PromiseReaction"; + case JSType::PROMISE_CAPABILITY: + return "PromiseCapability"; + case JSType::PROMISE_ITERATOR_RECORD: + return "PromiseIteratorRecord"; + case JSType::PROMISE_RECORD: + return "PromiseRecord"; + case JSType::RESOLVING_FUNCTIONS_RECORD: + return "ResolvingFunctionsRecord"; + case JSType::JS_PROMISE: + return "Promise"; + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + return "PromiseReactionsFunction"; + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + return "PromiseExecutorFunction"; + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + return "PromiseAllResolveElementFunction"; + case JSType::MICRO_JOB_QUEUE: + return "MicroJobQueue"; + case JSType::PENDING_JOB: + return "PendingJob"; + case JSType::COMPLETION_RECORD: + return "CompletionRecord"; + case JSType::GLOBAL_ENV: + return "GlobalEnv"; + case JSType::ACCESSOR_DATA: + return "AccessorData"; + case JSType::INTERNAL_ACCESSOR: + return "InternalAccessor"; + case JSType::SYMBOL: + return "Symbol"; + case JSType::OBJECT_WRAPPER: + return "ObjectWapper"; + case JSType::PROPERTY_BOX: + return "PropertyBox"; + case JSType::JS_ASYNC_FUNCTION: + return "AsyncFunction"; + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + return "AsyncAwaitStatusFunction"; + case JSType::JS_ASYNC_FUNC_OBJECT: + return "AsyncFunctionObject"; + default: { + CString ret = "unknown type "; + return ret + static_cast(type); + } + } +} + +static void DumpArrayClass(JSThread *thread, const TaggedArray *arr, std::ostream &os) +{ + DISALLOW_GARBAGE_COLLECTION; + array_size_t len = arr->GetLength(); + os << " \n"; + for (array_size_t i = 0; i < len; i++) { + JSTaggedValue val(arr->Get(i)); + if (!val.IsHole()) { + os << std::right << std::setw(DUMP_PROPERTY_OFFSET) << i << ": "; + val.DumpTaggedValue(thread, os); + os << "\n"; + } + } +} + +static void DumpStringClass(const EcmaString *str, std::ostream &os) +{ + DISALLOW_GARBAGE_COLLECTION; + CString string = ConvertToString(str); + os << string; +} + +static void DumpPropertyKey(JSTaggedValue key, std::ostream &os) +{ + if (key.IsString()) { + DumpStringClass(EcmaString::Cast(key.GetTaggedObject()), os); + } else if (key.IsSymbol()) { + JSSymbol *sym = JSSymbol::Cast(key.GetTaggedObject()); + DumpStringClass(EcmaString::Cast(sym->GetDescription().GetTaggedObject()), os); + } else { + UNREACHABLE(); + } +} + +static void DumpHClass(JSThread *thread, const JSHClass *jshclass, std::ostream &os, bool withDetail) +{ + DISALLOW_GARBAGE_COLLECTION; + os << "JSHClass :" << std::setw(DUMP_TYPE_OFFSET); + os << "Type :" << JSHClass::DumpJSType(jshclass->GetObjectType()) << "\n"; + + os << " - Prototype :" << std::setw(DUMP_TYPE_OFFSET); + jshclass->GetPrototype().DumpTaggedValue(thread, os); + os << "\n"; + os << " - PropertyDescriptors :" << std::setw(DUMP_TYPE_OFFSET); + JSTaggedValue attrs = jshclass->GetAttributes(); + attrs.DumpTaggedValue(thread, os); + os << "\n"; + if (withDetail && !attrs.IsNull()) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject()); + layoutInfo->Dump(thread, os); + } + os << " - Transitions :" << std::setw(DUMP_TYPE_OFFSET); + JSTaggedValue transtions = jshclass->GetTransitions(); + transtions.DumpTaggedValue(thread, os); + os << "\n"; + if (withDetail && !transtions.IsNull()) { + transtions.Dump(thread, os); + } + os << " - Parent :" << std::setw(DUMP_TYPE_OFFSET); + jshclass->GetParent().DumpTaggedValue(thread, os); + os << "\n"; + + os << " - Flags : " << std::setw(DUMP_TYPE_OFFSET); + os << "Ctor :" << jshclass->IsConstructor(); + os << "| Callable :" << jshclass->IsCallable(); + os << "| Extensible :" << jshclass->IsExtensible(); + os << "| ElementRepresentation :" << static_cast(jshclass->GetElementRepresentation()); + os << "| UnusedInlineProperties :" << std::dec << jshclass->GetUnusedInlinedProps(); + os << "| UnusedOutProperties :" << std::dec << jshclass->GetUnusedNonInlinedProps(); + os << "\n"; +} + +static void DumpDynClass(JSThread *thread, TaggedObject *obj, std::ostream &os) +{ + JSHClass *hclass = obj->GetClass(); + os << "JSHClass :" << std::setw(DUMP_TYPE_OFFSET) << " klass_(" << std::hex << hclass << ")\n"; + DumpHClass(thread, hclass, os, true); +} + +static void DumpAttr(const PropertyAttributes &attr, bool fastMode, std::ostream &os) +{ + if (attr.IsAccessor()) { + os << "(Accessor) "; + } + + os << "Attr("; + if (attr.IsNoneAttributes()) { + os << "NONE"; + } + if (attr.IsWritable()) { + os << "W"; + } + if (attr.IsEnumerable()) { + os << "E"; + } + if (attr.IsConfigurable()) { + os << "C"; + } + os << ")"; + + os << " InlinedProps: " << attr.IsInlinedProps(); + + if (fastMode) { + os << " Order: " << std::dec << attr.GetOffset(); + os << " SortedIndex: " << std::dec << attr.GetSortedIndex(); + } else { + os << " Order: " << std::dec << attr.GetDictionaryOrder(); + } +} + +static void DumpObject(JSThread *thread, TaggedObject *obj, std::ostream &os) +{ + DISALLOW_GARBAGE_COLLECTION; + auto jsHclass = obj->GetClass(); + JSType type = jsHclass->GetObjectType(); + + switch (type) { + case JSType::HCLASS: + return DumpDynClass(thread, obj, os); + case JSType::TAGGED_ARRAY: + case JSType::TAGGED_DICTIONARY: + DumpArrayClass(thread, TaggedArray::Cast(obj), os); + break; + case JSType::STRING: + DumpStringClass(EcmaString::Cast(obj), os); + os << "\n"; + break; + case JSType::JS_OBJECT: + case JSType::JS_GLOBAL_OBJECT: + case JSType::JS_ERROR: + case JSType::JS_EVAL_ERROR: + case JSType::JS_RANGE_ERROR: + case JSType::JS_TYPE_ERROR: + case JSType::JS_REFERENCE_ERROR: + case JSType::JS_URI_ERROR: + case JSType::JS_SYNTAX_ERROR: + case JSType::JS_ARGUMENTS: + case JSType::JS_FUNCTION_BASE: + JSObject::Cast(obj)->Dump(thread, os); + break; + case JSType::GLOBAL_ENV: + GlobalEnv::Cast(obj)->Dump(thread, os); + break; + case JSType::ACCESSOR_DATA: + break; + case JSType::JS_FUNCTION: + JSFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_BOUND_FUNCTION: + JSBoundFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_SET: + JSSet::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_MAP: + JSMap::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_WEAK_SET: + JSWeakSet::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_WEAK_MAP: + JSWeakMap::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_REG_EXP: + JSRegExp::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_DATE: + JSDate::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ARRAY: + JSArray::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_TYPED_ARRAY: + JSTypedArray::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PROXY: + JSProxy::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PRIMITIVE_REF: + JSPrimitiveRef::Cast(obj)->Dump(thread, os); + break; + case JSType::SYMBOL: + JSSymbol::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_DATA_VIEW: + JSDataView::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ARRAY_BUFFER: + JSArrayBuffer::Cast(obj)->Dump(thread, os); + break; + case JSType::PROMISE_REACTIONS: + PromiseReaction::Cast(obj)->Dump(thread, os); + break; + case JSType::PROMISE_CAPABILITY: + PromiseCapability::Cast(obj)->Dump(thread, os); + break; + case JSType::PROMISE_ITERATOR_RECORD: + PromiseIteratorRecord::Cast(obj)->Dump(thread, os); + break; + case JSType::PROMISE_RECORD: + PromiseRecord::Cast(obj)->Dump(thread, os); + break; + case JSType::RESOLVING_FUNCTIONS_RECORD: + ResolvingFunctionsRecord::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PROMISE: + JSPromise::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + JSPromiseReactionsFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + JSPromiseExecutorFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + JSPromiseAllResolveElementFunction::Cast(obj)->Dump(thread); + break; + case JSType::MICRO_JOB_QUEUE: + MicroJobQueue::Cast(obj)->Dump(thread, os); + break; + case JSType::PENDING_JOB: + PendingJob::Cast(obj)->Dump(thread, os); + break; + case JSType::COMPLETION_RECORD: + CompletionRecord::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PROXY_REVOC_FUNCTION: + JSProxyRevocFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ASYNC_FUNCTION: + JSAsyncFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + JSAsyncAwaitStatusFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_GENERATOR_FUNCTION: + JSGeneratorFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ITERATOR: + case JSType::JS_FORIN_ITERATOR: + case JSType::JS_MAP_ITERATOR: + case JSType::JS_SET_ITERATOR: + case JSType::JS_ARRAY_ITERATOR: + case JSType::JS_STRING_ITERATOR: + case JSType::OBJECT_WRAPPER: + case JSType::PROPERTY_BOX: + break; + default: + UNREACHABLE(); + break; + } + + DumpHClass(thread, jsHclass, os, false); +} + +void JSTaggedValue::DumpSpecialValue([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + ASSERT(IsSpecial()); + os << "[Special Value] : "; + switch (GetRawData()) { + case VALUE_HOLE: + os << "Hole"; + break; + case VALUE_NULL: + os << "Null"; + break; + case VALUE_FALSE: + os << "False"; + break; + case VALUE_TRUE: + os << "True"; + break; + case VALUE_UNDEFINED: + os << "Undefined"; + break; + case VALUE_EXCEPTION: + os << "Exception"; + break; + default: + UNREACHABLE(); + break; + } +} + +void JSTaggedValue::DumpHeapObjectType([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + ASSERT(IsWeak() || IsHeapObject()); + bool isWeak = IsWeak(); + TaggedObject *obj = isWeak ? GetTaggedWeakRef() : GetTaggedObject(); + if (isWeak) { + os << "----------Dump Weak Referent----------" + << "\n"; + } + + JSType type = GetTaggedObject()->GetClass()->GetObjectType(); + if (type == JSType::STRING) { + CString string = ConvertToString(EcmaString::Cast(obj)); + os << std::left << std::setw(DUMP_TYPE_OFFSET) << "[" + string + "]"; + } else { + std::ostringstream address; + address << obj; + CString addrStr = CString(address.str()); + + os << std::left << std::setw(DUMP_TYPE_OFFSET) << "[" + JSHClass::DumpJSType(type) + "(" + addrStr + ")]"; + } +} + +void JSTaggedValue::DumpTaggedValue(JSThread *thread, std::ostream &os) const +{ + if (IsInt()) { + os << std::left << std::setw(DUMP_TYPE_OFFSET) << "[Int] : " << std::hex << "0x" << GetInt() << std::dec << " (" + << GetInt() << ")"; + } else if (IsDouble()) { + os << std::left << std::setw(DUMP_TYPE_OFFSET) << "[Double] : " << GetDouble(); + } else if (IsSpecial()) { + DumpSpecialValue(thread, os); + } else { + DumpHeapObjectType(thread, os); + } +} + +void JSTaggedValue::Dump(JSThread *thread, std::ostream &os) const +{ + DumpTaggedValue(thread, os); + os << "\n"; + + if (IsHeapObject()) { + TaggedObject *obj = GetTaggedObject(); + DumpObject(thread, obj, os); + } +} + +void JSTaggedValue::Dump(JSThread *thread) const +{ + Dump(thread, std::cout); +} + +void JSTaggedValue::DumpVal(JSThread *thread, JSTaggedType val) +{ + JSTaggedValue(val).Dump(thread); +} + +void JSThread::DumpStack() +{ + EcmaFrameHandler handler(this); + handler.DumpStack(std::cout); +} + +void NumberDictionary::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + JSTaggedValue val(GetValue(hashIndex)); + os << std::right << std::setw(DUMP_PROPERTY_OFFSET) + << static_cast(JSTaggedNumber(key).GetNumber()) << ": "; + val.DumpTaggedValue(thread, os); + os << " "; + DumpAttr(GetAttributes(hashIndex), false, os); + os << "\n"; + } + } +} + +void NameDictionary::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + JSTaggedValue val(GetValue(hashIndex)); + os << std::right << std::setw(DUMP_PROPERTY_OFFSET); + DumpPropertyKey(key, os); + os << ": "; + val.DumpTaggedValue(thread, os); + os << " "; + DumpAttr(GetAttributes(hashIndex), false, os); + os << "\n"; + } + } +} + +void GlobalDictionary::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + JSTaggedValue val(GetValue(hashIndex)); + os << std::right << std::setw(DUMP_PROPERTY_OFFSET); + DumpPropertyKey(key, os); + os << " : "; + val.DumpTaggedValue(thread, os); + os << " "; + DumpAttr(GetAttributes(hashIndex), false, os); + os << "\n"; + } + } +} + +void LayoutInfo::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int num = NumberOfElements(); + for (int i = 0; i < num; i++) { + JSTaggedValue key = GetKey(i); + PropertyAttributes attr = GetAttr(i); + os << std::right << std::setw(DUMP_PROPERTY_OFFSET); + os << "[" << i << "]: "; + DumpPropertyKey(key, os); + os << " : "; + DumpAttr(attr, true, os); + os << "\n"; + } +} + +void TransitionsDictionary::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + os << std::right << std::setw(DUMP_PROPERTY_OFFSET); + DumpPropertyKey(key, os); + os << " : "; + GetValue(hashIndex).DumpTaggedValue(thread, os); + os << " : "; + GetAttributes(hashIndex).DumpTaggedValue(thread, os); + os << "\n"; + } + } +} + +void LinkedHashSet::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfElements() + NumberOfDeletedElements(); + for (int hashIndex = 0; hashIndex < capacity; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + os << std::right << std::setw(DUMP_PROPERTY_OFFSET); + key.DumpTaggedValue(thread, os); + os << "\n"; + } + } +} + +void LinkedHashMap::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfElements() + NumberOfDeletedElements(); + for (int hashIndex = 0; hashIndex < capacity; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + JSTaggedValue val(GetValue(hashIndex)); + os << std::right << std::setw(DUMP_PROPERTY_OFFSET); + key.DumpTaggedValue(thread, os); + os << ": "; + val.DumpTaggedValue(thread, os); + os << "\n"; + } + } +} + +void JSObject::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + JSHClass *jshclass = GetJSHClass(); + os << " - hclass: " << std::hex << jshclass << "\n"; + os << " - prototype: "; + jshclass->GetPrototype().DumpTaggedValue(thread, os); + os << "\n"; + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + os << " - elements: " << std::hex << elements; + if (elements->GetLength() == 0) { + os << " NONE\n"; + } else if (!elements->IsDictionaryMode()) { + DumpArrayClass(thread, elements, os); + } else { + NumberDictionary *dict = NumberDictionary::Cast(elements); + os << " EntriesCount() << "]>\n"; + dict->Dump(thread, os); + } + + TaggedArray *properties = TaggedArray::Cast(GetProperties().GetTaggedObject()); + os << " - properties: " << std::hex << properties; + if (IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(properties); + os << " EntriesCount() << "]>\n"; + dict->Dump(thread, os); + return; + } + + if (!properties->IsDictionaryMode()) { + JSTaggedValue attrs = jshclass->GetAttributes(); + if (attrs.IsNull()) { + return; + } + + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject()); + int propNumber = jshclass->GetPropertiesNumber(); + os << " \n"; + for (int i = 0; i < propNumber; i++) { + JSTaggedValue key = layoutInfo->GetKey(i); + PropertyAttributes attr = layoutInfo->GetAttr(i); + ASSERT(i == static_cast(attr.GetOffset())); + os << " " << std::right << std::setw(DUMP_PROPERTY_OFFSET); + DumpPropertyKey(key, os); + os << ": ("; + JSTaggedValue val; + if (attr.IsInlinedProps()) { + val = GetPropertyInlinedProps(i); + } else { + val = properties->Get(i - JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + } + val.DumpTaggedValue(thread, os); + os << ") "; + DumpAttr(attr, true, os); + os << "\n"; + } + } else { + NameDictionary *dict = NameDictionary::Cast(properties); + os << " EntriesCount() << "]>\n"; + dict->Dump(thread, os); + } +} + +void AccessorData::Dump(JSThread *thread, std::ostream &os) const +{ + auto *hclass = GetClass(); + if (hclass->GetObjectType() == JSType::INTERNAL_ACCESSOR) { + os << " - Getter: " << reinterpret_cast(GetGetter().GetTaggedObject()) << "\n"; + os << " - Setter: " << reinterpret_cast(GetSetter().GetTaggedObject()) << "\n"; + return; + } + + os << " - Getter: "; + GetGetter().DumpTaggedValue(thread, os); + os << "\n"; + + os << " - Setter: "; + GetSetter().DumpTaggedValue(thread, os); + os << "\n"; +} + +void Program::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - Location: "; + GetLocation().DumpTaggedValue(thread, os); + os << "\n"; + os << " - ConstantPool: "; + TaggedArray *arr = TaggedArray::Cast(GetConstantPool().GetTaggedObject()); + DumpArrayClass(thread, arr, os); + os << "\n"; + + os << " - MainFunction: "; + GetMainFunction().DumpTaggedValue(thread, os); + os << "\n"; +} + +void ConstantPool::Dump(JSThread *thread, std::ostream &os) const +{ + DumpArrayClass(thread, this, os); +} + +void JSFunction::Dump(JSThread *thread, std::ostream &os) const +{ + JSObject::Dump(thread, os); +} + +void JSHClass::Dump(JSThread *thread, std::ostream &os) const +{ + DumpHClass(thread, this, os, true); +} + +void JSBoundFunction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - BoundTarget: "; + GetBoundTarget().DumpTaggedValue(thread, os); + os << "\n"; + + os << " - BoundThis: "; + GetBoundThis().DumpTaggedValue(thread, os); + os << "\n"; + + os << " - BoundArguments: "; + GetBoundArguments().DumpTaggedValue(thread, os); + os << "\n"; + + JSObject::Dump(thread, os); +} + +void JSPrimitiveRef::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - SubValue : "; + GetValue().DumpTaggedValue(thread, os); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSDate::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - time: " << GetTime().GetDouble() << "\n"; + os << " - localOffset: " << GetLocalOffset().GetDouble() << "\n"; + JSObject::Dump(thread, os); +} + +void JSMap::Dump(JSThread *thread, std::ostream &os) const +{ + LinkedHashMap *map = LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject()); + os << " - elements: " << std::dec << map->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << map->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << map->Capacity() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + map->Dump(thread, os); +} + +void JSMapIterator::Dump(JSThread *thread, std::ostream &os) const +{ + LinkedHashMap *map = LinkedHashMap::Cast(GetIteratedMap().GetTaggedObject()); + os << " - elements: " << std::dec << map->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << map->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << map->Capacity() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex().GetInt() << "\n"; + os << " - IterationKind: " << std::dec << GetIterationKind().GetInt() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + map->Dump(thread, os); +} + +void JSSet::Dump(JSThread *thread, std::ostream &os) const +{ + LinkedHashSet *set = LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject()); + os << " - elements: " << std::dec << set->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << set->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << set->Capacity() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + set->Dump(thread, os); +} + +void JSWeakMap::Dump(JSThread *thread, std::ostream &os) const +{ + LinkedHashMap *map = LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject()); + os << " - length: " << std::dec << GetSize() << "\n"; + os << " - elements: " << std::dec << map->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << map->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << map->Capacity() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + map->Dump(thread, os); +} + +void JSWeakSet::Dump(JSThread *thread, std::ostream &os) const +{ + LinkedHashSet *set = LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject()); + os << " - size: " << std::dec << GetSize() << "\n"; + os << " - elements: " << std::dec << set->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << set->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << set->Capacity() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + set->Dump(thread, os); +} + +void JSSetIterator::Dump(JSThread *thread, std::ostream &os) const +{ + LinkedHashSet *set = LinkedHashSet::Cast(GetIteratedSet().GetTaggedObject()); + os << " - elements: " << std::dec << set->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << set->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << set->Capacity() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex().GetInt() << "\n"; + os << " - IterationKind: " << std::dec << GetIterationKind().GetInt() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + set->Dump(thread, os); +} + +void JSArray::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - length: " << std::dec << GetArrayLength() << "\n"; + JSObject::Dump(thread, os); +} + +void JSArrayIterator::Dump(JSThread *thread, std::ostream &os) const +{ + JSArray *array = JSArray::Cast(GetIteratedArray().GetTaggedObject()); + os << " - length: " << std::dec << array->GetArrayLength() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex().GetInt() << "\n"; + os << " - IterationKind: " << std::dec << GetIterationKind().GetInt() << "\n"; + JSObject::Dump(thread, os); +} + +void JSTypedArray::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - viewed-array-buffer: "; + GetViewedArrayBuffer().Dump(thread); + os << " - typed-array-name: "; + GetTypedArrayName().Dump(thread); + os << " - byte-length: "; + GetByteLength().Dump(thread); + os << " - byte-offset: "; + GetByteOffset().Dump(thread); + os << " - array-length: "; + GetArrayLength().Dump(thread); + JSObject::Dump(thread, os); +} + +void JSInt8Array::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - viewed-array-buffer: "; + GetViewedArrayBuffer().Dump(thread); + os << " - typed-array-name: "; + GetTypedArrayName().Dump(thread); + os << " - byte-length: "; + GetByteLength().Dump(thread); + os << " - byte-offset: "; + GetByteOffset().Dump(thread); + os << " - array-length: "; + GetArrayLength().Dump(thread); + JSObject::Dump(thread, os); +} + +void JSUint8Array::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - viewed-array-buffer: "; + GetViewedArrayBuffer().Dump(thread); + os << " - typed-array-name: "; + GetTypedArrayName().Dump(thread); + os << " - byte-length: "; + GetByteLength().Dump(thread); + os << " - byte-offset: "; + GetByteOffset().Dump(thread); + os << " - array-length: "; + GetArrayLength().Dump(thread); + JSObject::Dump(thread, os); +} + +void JSUint8ClampedArray::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - viewed-array-buffer: "; + GetViewedArrayBuffer().Dump(thread); + os << " - typed-array-name: "; + GetTypedArrayName().Dump(thread); + os << " - byte-length: "; + GetByteLength().Dump(thread); + os << " - byte-offset: "; + GetByteOffset().Dump(thread); + os << " - array-length: "; + GetArrayLength().Dump(thread); + JSObject::Dump(thread, os); +} + +void JSInt16Array::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - viewed-array-buffer: "; + GetViewedArrayBuffer().Dump(thread); + os << " - typed-array-name: "; + GetTypedArrayName().Dump(thread); + os << " - byte-length: "; + GetByteLength().Dump(thread); + os << " - byte-offset: "; + GetByteOffset().Dump(thread); + os << " - array-length: "; + GetArrayLength().Dump(thread); + JSObject::Dump(thread, os); +} + +void JSUint16Array::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - viewed-array-buffer: "; + GetViewedArrayBuffer().Dump(thread); + os << " - typed-array-name: "; + GetTypedArrayName().Dump(thread); + os << " - byte-length: "; + GetByteLength().Dump(thread); + os << " - byte-offset: "; + GetByteOffset().Dump(thread); + os << " - array-length: "; + GetArrayLength().Dump(thread); + JSObject::Dump(thread, os); +} + +void JSInt32Array::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - viewed-array-buffer: "; + GetViewedArrayBuffer().Dump(thread); + os << " - typed-array-name: "; + GetTypedArrayName().Dump(thread); + os << " - byte-length: "; + GetByteLength().Dump(thread); + os << " - byte-offset: "; + GetByteOffset().Dump(thread); + os << " - array-length: "; + GetArrayLength().Dump(thread); + JSObject::Dump(thread, os); +} + +void JSUint32Array::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - viewed-array-buffer: "; + GetViewedArrayBuffer().Dump(thread); + os << " - typed-array-name: "; + GetTypedArrayName().Dump(thread); + os << " - byte-length: "; + GetByteLength().Dump(thread); + os << " - byte-offset: "; + GetByteOffset().Dump(thread); + os << " - array-length: "; + GetArrayLength().Dump(thread); + JSObject::Dump(thread, os); +} + +void JSFloat32Array::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - viewed-array-buffer: "; + GetViewedArrayBuffer().Dump(thread); + os << " - typed-array-name: "; + GetTypedArrayName().Dump(thread); + os << " - byte-length: "; + GetByteLength().Dump(thread); + os << " - byte-offset: "; + GetByteOffset().Dump(thread); + os << " - array-length: "; + GetArrayLength().Dump(thread); + JSObject::Dump(thread, os); +} + +void JSFloat64Array::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - viewed-array-buffer: "; + GetViewedArrayBuffer().Dump(thread); + os << " - typed-array-name: "; + GetTypedArrayName().Dump(thread); + os << " - byte-length: "; + GetByteLength().Dump(thread); + os << " - byte-offset: "; + GetByteOffset().Dump(thread); + os << " - array-length: "; + GetArrayLength().Dump(thread); +} + +void JSRegExp::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - source: "; + DumpStringClass(EcmaString::Cast(GetOriginalSource().GetTaggedObject()), os); + os << "\n"; + os << " - flags: "; + DumpStringClass(EcmaString::Cast(GetOriginalFlags().GetTaggedObject()), os); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSProxy::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - Target: "; + os << "\n"; + JSObject::Cast(GetTarget().GetTaggedObject())->Dump(thread, os); + os << " - Handler: "; + os << "\n"; + JSObject::Cast(GetHandler().GetTaggedObject())->Dump(thread, os); + os << "\n"; +} + +void JSSymbol::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - hash-field: "; + JSTaggedValue hashField = GetHashField(); + hashField.Dump(thread); + os << " - flags: "; + JSTaggedValue flags = GetFlags(); + flags.Dump(thread); + os << " - description: "; + JSTaggedValue description = GetDescription(); + description.Dump(thread); +} + +void LexicalEnv::Dump(JSThread *thread, std::ostream &os) const +{ + DumpArrayClass(thread, this, os); +} + +// NOLINTNEXTLINE(readability-function-size) +void GlobalEnv::Dump(JSThread *thread, std::ostream &os) const +{ + auto globalConst = thread->GlobalConstants(); + os << " - ObjectFunction: "; + GetObjectFunction().GetTaggedValue().Dump(thread, os); + os << " - FunctionFunction: "; + GetFunctionFunction().GetTaggedValue().Dump(thread, os); + os << " - NumberFunction: "; + GetNumberFunction().GetTaggedValue().Dump(thread, os); + os << " - DateFunction: "; + GetDateFunction().GetTaggedValue().Dump(thread, os); + os << " - BooleanFunction: "; + GetBooleanFunction().GetTaggedValue().Dump(thread, os); + os << " - ErrorFunction: "; + GetErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - ArrayFunction: "; + GetArrayFunction().GetTaggedValue().Dump(thread, os); + os << " - TypedArrayFunction: "; + GetTypedArrayFunction().GetTaggedValue().Dump(thread, os); + os << " - Int8ArrayFunction: "; + GetInt8ArrayFunction().GetTaggedValue().Dump(thread, os); + os << " - Uint8ArrayFunction: "; + GetUint8ArrayFunction().GetTaggedValue().Dump(thread, os); + os << " - Uint8ClampedArrayFunction: "; + GetUint8ClampedArrayFunction().GetTaggedValue().Dump(thread, os); + os << " - Int16ArrayFunction: "; + GetInt16ArrayFunction().GetTaggedValue().Dump(thread, os); + os << " - ArrayBufferFunction: "; + GetArrayBufferFunction().GetTaggedValue().Dump(thread, os); + os << " - SymbolFunction: "; + GetSymbolFunction().GetTaggedValue().Dump(thread, os); + os << " - RangeErrorFunction: "; + GetRangeErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - ReferenceErrorFunction: "; + GetReferenceErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - TypeErrorFunction: "; + GetTypeErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - URIErrorFunction: "; + GetURIErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - SyntaxErrorFunction: "; + GetSyntaxErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - EvalErrorFunction: "; + GetEvalErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - RegExpFunction: "; + GetRegExpFunction().GetTaggedValue().Dump(thread, os); + os << " - BuiltinsSetFunction: "; + GetBuiltinsSetFunction().GetTaggedValue().Dump(thread, os); + os << " - BuiltinsMapFunction: "; + GetBuiltinsMapFunction().GetTaggedValue().Dump(thread, os); + os << " - BuiltinsWeakSetFunction: "; + GetBuiltinsWeakSetFunction().GetTaggedValue().Dump(thread, os); + os << " - BuiltinsWeakMapFunction: "; + GetBuiltinsWeakMapFunction().GetTaggedValue().Dump(thread, os); + os << " - MathFunction: "; + GetMathFunction().GetTaggedValue().Dump(thread, os); + os << " - JsonFunction: "; + GetJsonFunction().GetTaggedValue().Dump(thread, os); + os << " - StringFunction: "; + GetStringFunction().GetTaggedValue().Dump(thread, os); + os << " - ProxyFunction: "; + GetProxyFunction().GetTaggedValue().Dump(thread, os); + os << " - ReflectFunction: "; + GetReflectFunction().GetTaggedValue().Dump(thread, os); + os << " - AsyncFunction: "; + GetAsyncFunction().GetTaggedValue().Dump(thread, os); + os << " - AsyncFunctionPrototype: "; + GetAsyncFunctionPrototype().GetTaggedValue().Dump(thread, os); + os << " - JSGlobalObject: "; + GetJSGlobalObject().GetTaggedValue().Dump(thread, os); + os << " - EmptyArray: "; + GetEmptyArray().GetTaggedValue().Dump(thread, os); + os << " - EmptyString "; + globalConst->GetEmptyString().Dump(thread, os); + os << " - EmptyTaggedQueue: "; + GetEmptyTaggedQueue().GetTaggedValue().Dump(thread, os); + os << " - PrototypeString: "; + globalConst->GetPrototypeString().Dump(thread, os); + os << " - HasInstanceSymbol: "; + GetHasInstanceSymbol().GetTaggedValue().Dump(thread, os); + os << " - IsConcatSpreadableSymbol: "; + GetIsConcatSpreadableSymbol().GetTaggedValue().Dump(thread, os); + os << " - ToStringTagSymbol: "; + GetToStringTagSymbol().GetTaggedValue().Dump(thread, os); + os << " - IteratorSymbol: "; + GetIteratorSymbol().GetTaggedValue().Dump(thread, os); + os << " - MatchSymbol: "; + GetMatchSymbol().GetTaggedValue().Dump(thread, os); + os << " - ReplaceSymbol: "; + GetReplaceSymbol().GetTaggedValue().Dump(thread, os); + os << " - SearchSymbol: "; + GetSearchSymbol().GetTaggedValue().Dump(thread, os); + os << " - SpeciesSymbol: "; + GetSpeciesSymbol().GetTaggedValue().Dump(thread, os); + os << " - SplitSymbol: "; + GetSplitSymbol().GetTaggedValue().Dump(thread, os); + os << " - ToPrimitiveSymbol: "; + GetToPrimitiveSymbol().GetTaggedValue().Dump(thread, os); + os << " - UnscopablesSymbol: "; + GetUnscopablesSymbol().GetTaggedValue().Dump(thread, os); + os << " - HoleySymbol: "; + GetHoleySymbol().GetTaggedValue().Dump(thread, os); + os << " - ConstructorString: "; + globalConst->GetConstructorString().Dump(thread, os); + os << " - IteratorPrototype: "; + GetIteratorPrototype().GetTaggedValue().Dump(thread, os); + os << " - ForinIteratorPrototype: "; + GetForinIteratorPrototype().GetTaggedValue().Dump(thread, os); + os << " - StringIterator: "; + GetStringIterator().GetTaggedValue().Dump(thread, os); + os << " - MapIteratorPrototype: "; + GetMapIteratorPrototype().GetTaggedValue().Dump(thread, os); + os << " - SetIteratorPrototype: "; + GetSetIteratorPrototype().GetTaggedValue().Dump(thread, os); + os << " - ArrayIteratorPrototype: "; + GetArrayIteratorPrototype().GetTaggedValue().Dump(thread, os); + os << " - StringIteratorPrototype: "; + GetStringIteratorPrototype().GetTaggedValue().Dump(thread, os); + os << " - LengthString: "; + globalConst->GetLengthString().Dump(thread, os); + os << " - ValueString: "; + globalConst->GetValueString().Dump(thread, os); + os << " - WritableString: "; + globalConst->GetWritableString().Dump(thread, os); + os << " - GetString: "; + globalConst->GetGetString().Dump(thread, os); + os << " - SetString: "; + globalConst->GetSetString().Dump(thread, os); + os << " - EnumerableString: "; + globalConst->GetEnumerableString().Dump(thread, os); + os << " - ConfigurableString: "; + globalConst->GetConfigurableString().Dump(thread, os); + os << " - NameString: "; + globalConst->GetNameString().Dump(thread, os); + os << " - ValueOfString: "; + globalConst->GetValueOfString().Dump(thread, os); + os << " - ToStringString: "; + globalConst->GetToStringString().Dump(thread, os); + os << " - ToLocaleStringString: "; + globalConst->GetToLocaleStringString().Dump(thread, os); + os << " - UndefinedString: "; + globalConst->GetUndefinedString().Dump(thread, os); + os << " - NullString: "; + globalConst->GetNullString().Dump(thread, os); + os << " - TrueString: "; + globalConst->GetTrueString().Dump(thread, os); + os << " - FalseString: "; + globalConst->GetFalseString().Dump(thread, os); + os << " - RegisterSymbols: "; + GetRegisterSymbols().GetTaggedValue().Dump(thread, os); + os << " - ThrowTypeError: "; + GetThrowTypeError().GetTaggedValue().Dump(thread, os); + os << " - GetPrototypeOfString: "; + globalConst->GetGetPrototypeOfString().Dump(thread, os); + os << " - SetPrototypeOfString: "; + globalConst->GetSetPrototypeOfString().Dump(thread, os); + os << " - IsExtensibleString: "; + globalConst->GetIsExtensibleString().Dump(thread, os); + os << " - PreventExtensionsString: "; + globalConst->GetPreventExtensionsString().Dump(thread, os); + os << " - GetOwnPropertyDescriptorString: "; + globalConst->GetGetOwnPropertyDescriptorString().Dump(thread, os); + os << " - DefinePropertyString: "; + globalConst->GetDefinePropertyString().Dump(thread, os); + os << " - HasString: "; + globalConst->GetHasString().Dump(thread, os); + os << " - DeletePropertyString: "; + globalConst->GetDeletePropertyString().Dump(thread, os); + os << " - EnumerateString: "; + globalConst->GetEnumerateString().Dump(thread, os); + os << " - OwnKeysString: "; + globalConst->GetOwnKeysString().Dump(thread, os); + os << " - ApplyString: "; + globalConst->GetApplyString().Dump(thread, os); + os << " - ProxyString: "; + globalConst->GetProxyString().Dump(thread, os); + os << " - RevokeString: "; + globalConst->GetRevokeString().Dump(thread, os); + os << " - ProxyConstructString: "; + globalConst->GetProxyConstructString().Dump(thread, os); + os << " - ProxyCallString: "; + globalConst->GetProxyCallString().Dump(thread, os); + os << " - DoneString: "; + globalConst->GetDoneString().Dump(thread, os); + os << " - NegativeZeroString: "; + globalConst->GetNegativeZeroString().Dump(thread, os); + os << " - NextString: "; + globalConst->GetNextString().Dump(thread, os); + os << " - PromiseThenString: "; + globalConst->GetPromiseThenString().Dump(thread, os); + os << " - PromiseFunction: "; + GetPromiseFunction().GetTaggedValue().Dump(thread, os); + os << " - PromiseReactionJob: "; + GetPromiseReactionJob().GetTaggedValue().Dump(thread, os); + os << " - PromiseResolveThenableJob: "; + GetPromiseResolveThenableJob().GetTaggedValue().Dump(thread, os); + os << " - ScriptJobString: "; + globalConst->GetScriptJobString().Dump(thread, os); + os << " - PromiseString: "; + globalConst->GetPromiseString().Dump(thread, os); + os << " - IdentityString: "; + globalConst->GetIdentityString().Dump(thread, os); + os << " - AsyncFunctionString: "; + globalConst->GetAsyncFunctionString().Dump(thread, os); + os << " - ThrowerString: "; + globalConst->GetThrowerString().Dump(thread, os); + os << " - Undefined: "; + globalConst->GetUndefined().Dump(thread, os); +} + +void JSDataView::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - data-view: "; + GetDataView().Dump(thread); + os << " - buffer: "; + GetViewedArrayBuffer().Dump(thread); + os << " - byte-length: "; + GetByteLength().Dump(thread); + os << " - byte-offset: "; + GetByteOffset().Dump(thread); +} + +void JSArrayBuffer::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - byte-length: "; + GetArrayBufferByteLength().Dump(thread); + os << " - buffer-data: "; + GetArrayBufferData().Dump(thread); +} + +void PromiseReaction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - promise-capability: "; + GetPromiseCapability().Dump(thread); + os << " - type: "; + GetType().Dump(thread); + os << " - handler: "; + GetHandler().Dump(thread); +} + +void PromiseCapability::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - promise: "; + GetPromise().Dump(thread); + os << " - resolve: "; + GetResolve().Dump(thread); + os << " - reject: "; + GetReject().Dump(thread); +} + +void PromiseIteratorRecord::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - iterator: "; + GetIterator().Dump(thread); + os << " - done: "; + GetDone().Dump(thread); +} + +void PromiseRecord::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - value: "; + GetValue().Dump(thread); +} + +void ResolvingFunctionsRecord::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - resolve-function: "; + GetResolveFunction().Dump(thread); + os << " - reject-function: "; + GetRejectFunction().Dump(thread); +} + +void JSPromise::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - promise-state: "; + GetPromiseState().Dump(thread); + os << " - promise-result: "; + GetPromiseResult().Dump(thread); + os << " - promise-fulfill-reactions: "; + GetPromiseFulfillReactions().Dump(thread); + os << " - promise-reject-reactions: "; + GetPromiseRejectReactions().Dump(thread); + os << " - promise-is-handled: "; + GetPromiseIsHandled().Dump(thread); + JSObject::Dump(thread, os); +} + +void JSPromiseReactionsFunction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - promise: "; + GetPromise().Dump(thread); + os << " - already-resolved: "; + GetAlreadyResolved().Dump(thread); + JSObject::Dump(thread, os); +} + +void JSPromiseExecutorFunction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - capability: "; + GetCapability().Dump(thread); + JSObject::Dump(thread, os); +} + +void JSPromiseAllResolveElementFunction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - index: "; + GetIndex().Dump(thread); + os << " - values: "; + GetValues().Dump(thread); + os << " - capability: "; + GetCapabilities().Dump(thread); + os << " - remaining-elements: "; + GetRemainingElements().Dump(thread); + os << " - already-called: "; + GetAlreadyCalled().Dump(thread); + JSObject::Dump(thread, os); +} + +void MicroJobQueue::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - promise-job-queue: "; + GetPromiseJobQueue().Dump(thread); + os << " - script-job-queue: "; + GetScriptJobQueue().Dump(thread); +} + +void PendingJob::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - job: "; + GetJob().Dump(thread); + os << " - arguments: "; + GetArguments().Dump(thread); +} + +void CompletionRecord::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - type: "; + GetType().Dump(thread); + os << " - value: "; + GetValue().Dump(thread); +} + +void JSProxyRevocFunction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - RevocableProxy: "; + os << "\n"; + JSObject::Cast(GetRevocableProxy().GetTaggedObject())->Dump(thread, os); + os << "\n"; +} + +void JSAsyncFunction::Dump(JSThread *thread, std::ostream &os) const +{ + JSFunction::Dump(thread, os); +} + +void JSAsyncAwaitStatusFunction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - AsyncContext: "; + os << "\n"; + JSObject::Cast(GetAsyncContext().GetTaggedObject())->Dump(thread, os); + os << "\n"; +} + +void JSGeneratorFunction::Dump(JSThread *thread, std::ostream &os) const +{ + JSFunction::Dump(thread, os); +} + +// ######################################################################################## +// Dump for Snapshot +// ######################################################################################## +static void DumpArrayClass([[maybe_unused]] JSThread *thread, const TaggedArray *arr, + std::vector> &vec) +{ + DISALLOW_GARBAGE_COLLECTION; + array_size_t len = arr->GetLength(); + + for (array_size_t i = 0; i < len; i++) { + JSTaggedValue val(arr->Get(i)); + if (!val.IsHole()) { + CString str = ToCString(i); + vec.push_back(std::make_pair(str, val)); + } + } +} + +static void DumpDynClass([[maybe_unused]] JSThread *thread, TaggedObject *obj, + std::vector> &vec) +{ + JSHClass *jshclass = obj->GetClass(); + vec.push_back(std::make_pair("__proto__", jshclass->GetPrototype())); +} + +static void DumpObject(JSThread *thread, TaggedObject *obj, std::vector> &vec) +{ + DISALLOW_GARBAGE_COLLECTION; + auto jsHclass = obj->GetClass(); + JSType type = jsHclass->GetObjectType(); + + switch (type) { + case JSType::HCLASS: + DumpDynClass(thread, obj, vec); + break; + case JSType::TAGGED_ARRAY: + DumpArrayClass(thread, TaggedArray::Cast(obj), vec); + break; + case JSType::STRING: + case JSType::JS_NATIVE_POINTER: + break; + case JSType::JS_OBJECT: + case JSType::JS_ERROR: + case JSType::JS_EVAL_ERROR: + case JSType::JS_RANGE_ERROR: + case JSType::JS_TYPE_ERROR: + case JSType::JS_REFERENCE_ERROR: + case JSType::JS_URI_ERROR: + case JSType::JS_SYNTAX_ERROR: + case JSType::JS_ARGUMENTS: + JSObject::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_FUNCTION_BASE: + case JSType::JS_FUNCTION: + JSFunction::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_BOUND_FUNCTION: + JSBoundFunction::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_SET: + JSSet::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_MAP: + JSMap::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_WEAK_SET: + JSWeakSet::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_WEAK_MAP: + JSWeakMap::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_REG_EXP: + JSRegExp::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_DATE: + JSDate::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_ARRAY: + JSArray::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_TYPED_ARRAY: + JSTypedArray::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_PROXY: + JSProxy::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_PRIMITIVE_REF: + JSPrimitiveRef::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::SYMBOL: + JSSymbol::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::GLOBAL_ENV: + GlobalEnv::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::ACCESSOR_DATA: + case JSType::INTERNAL_ACCESSOR: + AccessorData::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_DATA_VIEW: + JSDataView::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::PROMISE_REACTIONS: + PromiseReaction::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::PROMISE_CAPABILITY: + PromiseCapability::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::PROMISE_ITERATOR_RECORD: + PromiseIteratorRecord::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::PROMISE_RECORD: + PromiseRecord::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::RESOLVING_FUNCTIONS_RECORD: + ResolvingFunctionsRecord::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_PROMISE: + JSPromise::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + JSPromiseReactionsFunction::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + JSPromiseExecutorFunction::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + JSPromiseAllResolveElementFunction::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::MICRO_JOB_QUEUE: + MicroJobQueue::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::PENDING_JOB: + PendingJob::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::COMPLETION_RECORD: + CompletionRecord::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_ITERATOR: + case JSType::JS_FORIN_ITERATOR: + case JSType::JS_MAP_ITERATOR: + case JSType::JS_SET_ITERATOR: + case JSType::JS_ARRAY_ITERATOR: + case JSType::JS_STRING_ITERATOR: + case JSType::JS_ARRAY_BUFFER: + JSArrayBuffer::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_PROXY_REVOC_FUNCTION: + JSProxyRevocFunction::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_ASYNC_FUNCTION: + JSAsyncFunction::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + JSAsyncAwaitStatusFunction::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::JS_GENERATOR_FUNCTION: + JSGeneratorFunction::Cast(obj)->DumpForSnapshot(thread, vec); + break; + case JSType::OBJECT_WRAPPER: + case JSType::TRANSITION_HANDLER: + case JSType::PROTOTYPE_HANDLER: + case JSType::PROPERTY_BOX: + break; + default: + break; + } +} + +static inline void EcmaStringToStd(CString &res, EcmaString *str) +{ + if (str->GetLength() == 0) { + CString emptyStr = "EmptyString"; + res.append(emptyStr); + } + + CString string = ConvertToString(str); + res.append(string); +} + +static inline void KeyToStd(CString &res, JSTaggedValue key) +{ + if (key.IsString()) { + EcmaStringToStd(res, EcmaString::Cast(key.GetTaggedObject())); + } else if (key.IsSymbol()) { + JSSymbol *sym = JSSymbol::Cast(key.GetTaggedObject()); + EcmaStringToStd(res, EcmaString::Cast(sym->GetDescription().GetTaggedObject())); + } else { + UNREACHABLE(); + } +} + +void JSTaggedValue::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + if (IsHeapObject()) { + return DumpObject(thread, GetTaggedObject(), vec); + } + + UNREACHABLE(); +} + +void NumberDictionary::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + JSTaggedValue val(GetValue(hashIndex)); + CString str = ToCString(static_cast(JSTaggedNumber(key).GetNumber())); + vec.push_back(std::make_pair(str, val)); + } + } +} + +void NameDictionary::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + JSTaggedValue val(GetValue(hashIndex)); + + CString str; + KeyToStd(str, key); + vec.push_back(std::make_pair(str, val)); + } + } +} + +void GlobalDictionary::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyBox *box = PropertyBox::Cast(key.GetTaggedObject()); + + CString str; + KeyToStd(str, key); + JSTaggedValue val = box->GetValue(); + vec.push_back(std::make_pair(str, val)); + } + } +} + +void LinkedHashSet::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfElements() + NumberOfDeletedElements(); + for (int hashIndex = 0; hashIndex < capacity; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + CString str; + KeyToStd(str, key); + vec.push_back(std::make_pair(str, JSTaggedValue::Hole())); + } + } +} + +void LinkedHashMap::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfElements() + NumberOfDeletedElements(); + for (int hashIndex = 0; hashIndex < capacity; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + JSTaggedValue val(GetValue(hashIndex)); + CString str; + KeyToStd(str, key); + vec.push_back(std::make_pair(str, val)); + } + } +} + +void JSObject::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + + JSHClass *jshclass = GetJSHClass(); + vec.push_back(std::make_pair("__proto__", jshclass->GetPrototype())); + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + if (elements->GetLength() == 0) { + } else if (!elements->IsDictionaryMode()) { + DumpArrayClass(thread, elements, vec); + } else { + NumberDictionary *dict = NumberDictionary::Cast(elements); + dict->DumpForSnapshot(thread, vec); + } + + TaggedArray *properties = TaggedArray::Cast(GetProperties().GetTaggedObject()); + if (IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(properties); + dict->DumpForSnapshot(thread, vec); + return; + } + + if (!properties->IsDictionaryMode()) { + JSTaggedValue attrs = jshclass->GetAttributes(); + if (attrs.IsNull()) { + return; + } + + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject()); + int propNumber = jshclass->GetPropertiesNumber(); + for (int i = 0; i < propNumber; i++) { + JSTaggedValue key = layoutInfo->GetKey(i); + PropertyAttributes attr = layoutInfo->GetAttr(i); + ASSERT(i == static_cast(attr.GetOffset())); + JSTaggedValue val; + if (attr.IsInlinedProps()) { + val = GetPropertyInlinedProps(i); + } else { + val = properties->Get(i - JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + } + + CString str; + KeyToStd(str, key); + vec.push_back(std::make_pair(str, val)); + } + } else { + NameDictionary *dict = NameDictionary::Cast(properties); + dict->DumpForSnapshot(thread, vec); + } +} + +void JSHClass::DumpForSnapshot([[maybe_unused]] JSThread *thread, + [[maybe_unused]] std::vector> &vec) const +{ +} + +void JSFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSObject::DumpForSnapshot(thread, vec); +} + +void Program::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("Location"), GetLocation())); + vec.push_back(std::make_pair(CString("ConstantPool"), GetConstantPool())); + vec.push_back(std::make_pair(CString("MainFunction"), GetMainFunction())); +} + +void ConstantPool::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DumpArrayClass(thread, this, vec); +} + +void JSBoundFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSObject::DumpForSnapshot(thread, vec); + + vec.push_back(std::make_pair(CString("BoundTarget"), GetBoundTarget())); + vec.push_back(std::make_pair(CString("BoundThis"), GetBoundThis())); + vec.push_back(std::make_pair(CString("BoundArguments"), GetBoundArguments())); +} + +void JSPrimitiveRef::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("subValue"), GetValue())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSDate::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("time"), GetTime())); + vec.push_back(std::make_pair(CString("localOffset"), GetLocalOffset())); + + JSObject::DumpForSnapshot(thread, vec); +} + +void JSMap::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + LinkedHashMap *map = LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject()); + map->DumpForSnapshot(thread, vec); + + JSObject::DumpForSnapshot(thread, vec); +} + +void JSMapIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + LinkedHashMap *map = LinkedHashMap::Cast(GetIteratedMap().GetTaggedObject()); + map->DumpForSnapshot(thread, vec); + vec.push_back(std::make_pair(CString("NextIndex"), GetNextIndex())); + vec.push_back(std::make_pair(CString("IterationKind"), GetIterationKind())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSSet::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + LinkedHashSet *set = LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject()); + set->DumpForSnapshot(thread, vec); + + JSObject::DumpForSnapshot(thread, vec); +} + +void JSWeakMap::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + LinkedHashMap *map = LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject()); + map->DumpForSnapshot(thread, vec); + + JSObject::DumpForSnapshot(thread, vec); +} + +void JSWeakSet::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + LinkedHashSet *set = LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject()); + set->DumpForSnapshot(thread, vec); + + JSObject::DumpForSnapshot(thread, vec); +} +void JSSetIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + LinkedHashSet *set = LinkedHashSet::Cast(GetIteratedSet().GetTaggedObject()); + set->DumpForSnapshot(thread, vec); + vec.push_back(std::make_pair(CString("NextIndex"), GetNextIndex())); + vec.push_back(std::make_pair(CString("IterationKind"), GetIterationKind())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSArray::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSObject::DumpForSnapshot(thread, vec); +} + +void JSArrayIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSArray *array = JSArray::Cast(GetIteratedArray().GetTaggedObject()); + array->DumpForSnapshot(thread, vec); + vec.push_back(std::make_pair(CString("NextIndex"), GetNextIndex())); + vec.push_back(std::make_pair(CString("IterationKind"), GetIterationKind())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSTypedArray::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("viewed-array-buffer"), GetViewedArrayBuffer())); + vec.push_back(std::make_pair(CString("typed-array-name"), GetTypedArrayName())); + vec.push_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.push_back(std::make_pair(CString("byte-offset"), GetByteOffset())); + vec.push_back(std::make_pair(CString("array-length"), GetArrayLength())); +} + +void JSInt8Array::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("viewed-array-buffer"), GetViewedArrayBuffer())); + vec.push_back(std::make_pair(CString("typed-array-name"), GetTypedArrayName())); + vec.push_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.push_back(std::make_pair(CString("byte-offset"), GetByteOffset())); + vec.push_back(std::make_pair(CString("array-length"), GetArrayLength())); +} + +void JSUint8Array::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("viewed-array-buffer"), GetViewedArrayBuffer())); + vec.push_back(std::make_pair(CString("typed-array-name"), GetTypedArrayName())); + vec.push_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.push_back(std::make_pair(CString("byte-offset"), GetByteOffset())); + vec.push_back(std::make_pair(CString("array-length"), GetArrayLength())); +} + +void JSUint8ClampedArray::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("viewed-array-buffer"), GetViewedArrayBuffer())); + vec.push_back(std::make_pair(CString("typed-array-name"), GetTypedArrayName())); + vec.push_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.push_back(std::make_pair(CString("byte-offset"), GetByteOffset())); + vec.push_back(std::make_pair(CString("array-length"), GetArrayLength())); +} + +void JSInt16Array::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("viewed-array-buffer"), GetViewedArrayBuffer())); + vec.push_back(std::make_pair(CString("typed-array-name"), GetTypedArrayName())); + vec.push_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.push_back(std::make_pair(CString("byte-offset"), GetByteOffset())); + vec.push_back(std::make_pair(CString("array-length"), GetArrayLength())); +} + +void JSUint16Array::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("viewed-array-buffer"), GetViewedArrayBuffer())); + vec.push_back(std::make_pair(CString("typed-array-name"), GetTypedArrayName())); + vec.push_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.push_back(std::make_pair(CString("byte-offset"), GetByteOffset())); + vec.push_back(std::make_pair(CString("array-length"), GetArrayLength())); +} + +void JSInt32Array::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("viewed-array-buffer"), GetViewedArrayBuffer())); + vec.push_back(std::make_pair(CString("typed-array-name"), GetTypedArrayName())); + vec.push_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.push_back(std::make_pair(CString("byte-offset"), GetByteOffset())); + vec.push_back(std::make_pair(CString("array-length"), GetArrayLength())); +} + +void JSUint32Array::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("viewed-array-buffer"), GetViewedArrayBuffer())); + vec.push_back(std::make_pair(CString("typed-array-name"), GetTypedArrayName())); + vec.push_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.push_back(std::make_pair(CString("byte-offset"), GetByteOffset())); + vec.push_back(std::make_pair(CString("array-length"), GetArrayLength())); +} + +void JSFloat32Array::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("viewed-array-buffer"), GetViewedArrayBuffer())); + vec.push_back(std::make_pair(CString("typed-array-name"), GetTypedArrayName())); + vec.push_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.push_back(std::make_pair(CString("byte-offset"), GetByteOffset())); + vec.push_back(std::make_pair(CString("array-length"), GetArrayLength())); +} + +void JSFloat64Array::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("viewed-array-buffer"), GetViewedArrayBuffer())); + vec.push_back(std::make_pair(CString("typed-array-name"), GetTypedArrayName())); + vec.push_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.push_back(std::make_pair(CString("byte-offset"), GetByteOffset())); + vec.push_back(std::make_pair(CString("array-length"), GetArrayLength())); +} + +void JSRegExp::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("originalSource"), GetOriginalSource())); + vec.push_back(std::make_pair(CString("originalFlags"), GetOriginalFlags())); + + JSObject::DumpForSnapshot(thread, vec); +} + +void JSProxy::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("target"), GetTarget())); + vec.push_back(std::make_pair(CString("handler"), GetHandler())); +} + +void JSSymbol::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("hash-field"), GetHashField())); + vec.push_back(std::make_pair(CString("flags"), GetFlags())); + vec.push_back(std::make_pair(CString("description"), GetDescription())); +} + +void AccessorData::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("getter"), GetGetter())); + vec.push_back(std::make_pair(CString("setter"), GetSetter())); +} + +void LexicalEnv::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DumpArrayClass(thread, this, vec); +} + +void GlobalEnv::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + auto globalConst = thread->GlobalConstants(); + vec.push_back(std::make_pair(CString("ObjectFunction"), GetObjectFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("FunctionFunction"), GetFunctionFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("NumberFunction"), GetNumberFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("DateFunction"), GetDateFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("BooleanFunction"), GetBooleanFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ErrorFunction"), GetErrorFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ArrayFunction"), GetArrayFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("TypedArrayFunction"), GetTypedArrayFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("Int8ArrayFunction"), GetInt8ArrayFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("Uint8ArrayFunction"), GetUint8ArrayFunction().GetTaggedValue())); + vec.push_back( + std::make_pair(CString("Uint8ClampedArrayFunction"), GetUint8ClampedArrayFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("Int16ArrayFunction"), GetInt16ArrayFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("Uint16ArrayFunction"), GetUint16ArrayFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("Int32ArrayFunction"), GetInt32ArrayFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("Uint32ArrayFunction"), GetUint32ArrayFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("Float32ArrayFunction"), GetFloat32ArrayFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("Float64ArrayFunction"), GetFloat64ArrayFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ArrayBufferFunction"), GetArrayBufferFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("SymbolFunction"), GetSymbolFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("RangeErrorFunction"), GetRangeErrorFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ReferenceErrorFunction"), GetReferenceErrorFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("TypeErrorFunction"), GetTypeErrorFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("URIErrorFunction"), GetURIErrorFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("SyntaxErrorFunction"), GetSyntaxErrorFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("EvalErrorFunction"), GetEvalErrorFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("RegExpFunction"), GetRegExpFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("BuiltinsSetFunction"), GetBuiltinsSetFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("BuiltinsMapFunction"), GetBuiltinsMapFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("BuiltinsWeakSetFunction"), GetBuiltinsWeakSetFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("BuiltinsWeakMapFunction"), GetBuiltinsWeakMapFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("MathFunction"), GetMathFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("JsonFunction"), GetJsonFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("StringFunction"), GetStringFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ProxyFunction"), GetProxyFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ReflectFunction"), GetReflectFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("AsyncFunction"), GetAsyncFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("AsyncFunctionPrototype"), GetAsyncFunctionPrototype().GetTaggedValue())); + vec.push_back(std::make_pair(CString("JSGlobalObject"), GetJSGlobalObject().GetTaggedValue())); + vec.push_back(std::make_pair(CString("EmptyArray"), GetEmptyArray().GetTaggedValue())); + vec.push_back(std::make_pair(CString("EmptyString"), globalConst->GetEmptyString())); + vec.push_back(std::make_pair(CString("EmptyTaggedQueue"), GetEmptyTaggedQueue().GetTaggedValue())); + vec.push_back(std::make_pair(CString("PrototypeString"), globalConst->GetPrototypeString())); + vec.push_back(std::make_pair(CString("HasInstanceSymbol"), GetHasInstanceSymbol().GetTaggedValue())); + vec.push_back(std::make_pair(CString("IsConcatSpreadableSymbol"), GetIsConcatSpreadableSymbol().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ToStringTagSymbol"), GetToStringTagSymbol().GetTaggedValue())); + vec.push_back(std::make_pair(CString("IteratorSymbol"), GetIteratorSymbol().GetTaggedValue())); + vec.push_back(std::make_pair(CString("MatchSymbol"), GetMatchSymbol().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ReplaceSymbol"), GetReplaceSymbol().GetTaggedValue())); + vec.push_back(std::make_pair(CString("SearchSymbol"), GetSearchSymbol().GetTaggedValue())); + vec.push_back(std::make_pair(CString("SpeciesSymbol"), GetSpeciesSymbol().GetTaggedValue())); + vec.push_back(std::make_pair(CString("SplitSymbol"), GetSplitSymbol().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ToPrimitiveSymbol"), GetToPrimitiveSymbol().GetTaggedValue())); + vec.push_back(std::make_pair(CString("UnscopablesSymbol"), GetUnscopablesSymbol().GetTaggedValue())); + vec.push_back(std::make_pair(CString("HoleySymbol"), GetHoleySymbol().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ConstructorString"), globalConst->GetConstructorString())); + vec.push_back(std::make_pair(CString("IteratorPrototype"), GetIteratorPrototype().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ForinIteratorPrototype"), GetForinIteratorPrototype().GetTaggedValue())); + vec.push_back(std::make_pair(CString("StringIterator"), GetStringIterator().GetTaggedValue())); + vec.push_back(std::make_pair(CString("MapIteratorPrototype"), GetMapIteratorPrototype().GetTaggedValue())); + vec.push_back(std::make_pair(CString("SetIteratorPrototype"), GetSetIteratorPrototype().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ArrayIteratorPrototype"), GetArrayIteratorPrototype().GetTaggedValue())); + vec.push_back(std::make_pair(CString("StringIteratorPrototype"), GetStringIteratorPrototype().GetTaggedValue())); + vec.push_back(std::make_pair(CString("LengthString"), globalConst->GetLengthString())); + vec.push_back(std::make_pair(CString("ValueString"), globalConst->GetValueString())); + vec.push_back(std::make_pair(CString("WritableString"), globalConst->GetWritableString())); + vec.push_back(std::make_pair(CString("GetString"), globalConst->GetGetString())); + vec.push_back(std::make_pair(CString("SetString"), globalConst->GetSetString())); + vec.push_back(std::make_pair(CString("EnumerableString"), globalConst->GetEnumerableString())); + vec.push_back(std::make_pair(CString("ConfigurableString"), globalConst->GetConfigurableString())); + vec.push_back(std::make_pair(CString("NameString"), globalConst->GetNameString())); + vec.push_back(std::make_pair(CString("ValueOfString"), globalConst->GetValueOfString())); + vec.push_back(std::make_pair(CString("ToStringString"), globalConst->GetToStringString())); + vec.push_back(std::make_pair(CString("ToLocaleStringString"), globalConst->GetToLocaleStringString())); + vec.push_back(std::make_pair(CString("UndefinedString"), globalConst->GetUndefinedString())); + vec.push_back(std::make_pair(CString("NullString"), globalConst->GetNullString())); + vec.push_back(std::make_pair(CString("TrueString"), globalConst->GetTrueString())); + vec.push_back(std::make_pair(CString("FalseString"), globalConst->GetFalseString())); + vec.push_back(std::make_pair(CString("RegisterSymbols"), GetRegisterSymbols().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ThrowTypeError"), GetThrowTypeError().GetTaggedValue())); + vec.push_back(std::make_pair(CString("GetPrototypeOfString"), globalConst->GetGetPrototypeOfString())); + vec.push_back(std::make_pair(CString("SetPrototypeOfString"), globalConst->GetSetPrototypeOfString())); + vec.push_back(std::make_pair(CString("IsExtensibleString"), globalConst->GetIsExtensibleString())); + vec.push_back(std::make_pair(CString("PreventExtensionsString"), globalConst->GetPreventExtensionsString())); + vec.push_back( + std::make_pair(CString("GetOwnPropertyDescriptorString"), globalConst->GetGetOwnPropertyDescriptorString())); + vec.push_back(std::make_pair(CString("DefinePropertyString"), globalConst->GetDefinePropertyString())); + vec.push_back(std::make_pair(CString("HasString"), globalConst->GetHasString())); + vec.push_back(std::make_pair(CString("DeletePropertyString"), globalConst->GetDeletePropertyString())); + vec.push_back(std::make_pair(CString("EnumerateString"), globalConst->GetEnumerateString())); + vec.push_back(std::make_pair(CString("OwnKeysString"), globalConst->GetOwnKeysString())); + vec.push_back(std::make_pair(CString("ApplyString"), globalConst->GetApplyString())); + vec.push_back(std::make_pair(CString("ProxyString"), globalConst->GetProxyString())); + vec.push_back(std::make_pair(CString("RevokeString"), globalConst->GetRevokeString())); + vec.push_back(std::make_pair(CString("ProxyConstructString"), globalConst->GetProxyConstructString())); + vec.push_back(std::make_pair(CString("ProxyCallString"), globalConst->GetProxyCallString())); + vec.push_back(std::make_pair(CString("DoneString"), globalConst->GetDoneString())); + vec.push_back(std::make_pair(CString("NegativeZeroString"), globalConst->GetNegativeZeroString())); + vec.push_back(std::make_pair(CString("NextString"), globalConst->GetNextString())); + vec.push_back(std::make_pair(CString("PromiseThenString"), globalConst->GetPromiseThenString())); + vec.push_back(std::make_pair(CString("PromiseFunction"), GetPromiseFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("PromiseReactionJob"), GetPromiseReactionJob().GetTaggedValue())); + vec.push_back( + std::make_pair(CString("PromiseResolveThenableJob"), GetPromiseResolveThenableJob().GetTaggedValue())); + vec.push_back(std::make_pair(CString("ScriptJobString"), globalConst->GetScriptJobString())); + vec.push_back(std::make_pair(CString("PromiseString"), globalConst->GetPromiseString())); + vec.push_back(std::make_pair(CString("IdentityString"), globalConst->GetIdentityString())); + vec.push_back(std::make_pair(CString("AsyncFunctionString"), globalConst->GetAsyncFunctionString())); + vec.push_back(std::make_pair(CString("ThrowerString"), globalConst->GetThrowerString())); + vec.push_back(std::make_pair(CString("Undefined"), globalConst->GetUndefined())); +} + +void JSDataView::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("data-view"), GetDataView())); + vec.push_back(std::make_pair(CString("buffer"), GetViewedArrayBuffer())); + vec.push_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.push_back(std::make_pair(CString("byte-offset"), GetByteOffset())); +} + +void JSArrayBuffer::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("byte-length"), GetArrayBufferByteLength())); + vec.push_back(std::make_pair(CString("buffer-data"), GetArrayBufferData())); +} + +void PromiseReaction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("promise-capability"), GetPromiseCapability())); + vec.push_back(std::make_pair(CString("type"), GetType())); + vec.push_back(std::make_pair(CString("handler"), GetHandler())); +} + +void PromiseCapability::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("promise"), GetPromise())); + vec.push_back(std::make_pair(CString("resolve"), GetResolve())); + vec.push_back(std::make_pair(CString("reject"), GetReject())); +} + +void PromiseIteratorRecord::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("iterator"), GetIterator())); + vec.push_back(std::make_pair(CString("done"), GetDone())); +} + +void PromiseRecord::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("value"), GetValue())); +} + +void ResolvingFunctionsRecord::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("resolve-function"), GetResolveFunction())); + vec.push_back(std::make_pair(CString("reject-function"), GetRejectFunction())); +} + +void JSPromise::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("promise-state"), GetPromiseState())); + vec.push_back(std::make_pair(CString("promise-result"), GetPromiseResult())); + vec.push_back(std::make_pair(CString("promise-fulfill-reactions"), GetPromiseFulfillReactions())); + vec.push_back(std::make_pair(CString("promise-reject-reactions"), GetPromiseRejectReactions())); + vec.push_back(std::make_pair(CString("promise-is-handled"), GetPromiseIsHandled())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSPromiseReactionsFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("promise"), GetPromise())); + vec.push_back(std::make_pair(CString("already-resolved"), GetAlreadyResolved())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSPromiseExecutorFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("capability"), GetCapability())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSPromiseAllResolveElementFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("index"), GetIndex())); + vec.push_back(std::make_pair(CString("values"), GetValues())); + vec.push_back(std::make_pair(CString("capabilities"), GetCapabilities())); + vec.push_back(std::make_pair(CString("remaining-elements"), GetRemainingElements())); + vec.push_back(std::make_pair(CString("already-called"), GetAlreadyCalled())); + JSObject::DumpForSnapshot(thread, vec); +} + +void MicroJobQueue::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("promise-job-queue"), GetPromiseJobQueue())); + vec.push_back(std::make_pair(CString("script-job-queue"), GetScriptJobQueue())); +} + +void PendingJob::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("job"), GetJob())); + vec.push_back(std::make_pair(CString("arguments"), GetArguments())); +} + +void CompletionRecord::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("type"), GetType())); + vec.push_back(std::make_pair(CString("value"), GetValue())); +} + +void JSProxyRevocFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("RevocableProxy"), GetRevocableProxy())); +} + +void JSAsyncFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSFunction::DumpForSnapshot(thread, vec); +} + +void JSAsyncAwaitStatusFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("AsyncContext"), GetAsyncContext())); +} + +void JSGeneratorFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSFunction::DumpForSnapshot(thread, vec); +} +} // namespace panda::ecmascript diff --git a/ecmascript/ecma_builtins.rb b/ecmascript/ecma_builtins.rb new file mode 100755 index 0000000000000000000000000000000000000000..c870e57512c646f36eeacb42e2fd6225f53cfaa2 --- /dev/null +++ b/ecmascript/ecma_builtins.rb @@ -0,0 +1,72 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +def ExtBuiltins.load_ecma_builtins(data) + ecma_fname = File.expand_path(File.join(File.dirname(__FILE__), 'ecma_runtime.yaml')) + builtins = YAML.load_file(ecma_fname) + builtins = JSON.parse(builtins.to_json, object_class: OpenStruct) + builtins.intrinsics.each do |intr| + new_intr = intr.signature.clone + + if !intr.signature['sig'] + new_intr['sig'] = intr.method_name + v_idx = 1 + imm_idx =2 # first imm is subcode, so other begin at 2 + new_intr.args.each_index do |i| + case new_intr.args[i] + when "any" + new_intr['sig'] << " v" + v_idx.to_s + ":in:any" + v_idx += 1 + when "acc" + next + when "u8", "i8", "u16", "i16", "u32", "i32" + new_intr['sig'] << " imm" + imm_idx.to_s + ":i32" + imm_idx += 1 + when "u64", "i64" + new_intr['sig'] << " imm" + imm_idx.to_s + ":i64" + imm_idx += 1 + when "string_id" + new_intr['sig'] << " string_id" + when "method_id" + new_intr['sig'] << " method_id" + else + raise "unknown the type in " + intr.method_name + end + end + end + + if new_intr['sig'].include?('v1:') && !new_intr['sig'].include?('v2:') + new_intr['sig'].sub!(/v1+?/, 'v') + end + + if !intr.signature['acc'] + if new_intr.ret == "any" then + new_intr['acc'] = "out:any" + elsif new_intr.ret == "void" then + new_intr['acc'] = "none" + else + raise "unknown the ret in " + intr.method_name + end + end + + if intr['exception'] == true + new_intr['exception'] = true + end + + new_intr['space'] = intr.space + + new_intr['name'] = intr.name + + data["builtins"] << new_intr + end +end \ No newline at end of file diff --git a/ecmascript/ecma_class_linker_extension.cpp b/ecmascript/ecma_class_linker_extension.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ac1c552f91dcc45d6b9ea7a633c1d627058a203b --- /dev/null +++ b/ecmascript/ecma_class_linker_extension.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_class_linker_extension.h" +#include "ecmascript/ecma_string.h" +#include "include/class_linker-inl.h" +#include "include/coretypes/class.h" + +namespace panda::ecmascript { +using SourceLang = panda_file::SourceLang; + +bool EcmaClassLinkerExtension::InitializeImpl(bool cmpStrEnabled) +{ + EcmaString::SetCompressedStringsEnabled(cmpStrEnabled); + return true; +} + +EcmaClassLinkerExtension::~EcmaClassLinkerExtension() +{ + if (!IsInitialized()) { + return; + } + FreeLoadedClasses(); +} + +void EcmaClassLinkerExtension::InitClasses(EcmaVM *vm) +{ + ASSERT(IsInitialized()); + vm_ = vm; + LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(GetLanguage()); + auto *objClass = NewClass(ctx.GetObjectClassDescriptor(), 0, 0, GetClassSize(ClassRoot::OBJECT)); + if (objClass == nullptr) { + return; + } + objClass->SetObjectSize(TaggedObject::ObjectHeaderSize()); + objClass->SetSourceLang(SourceLang::ECMASCRIPT); + objClass->SetState(Class::State::LOADED); + Runtime::GetCurrent()->GetClassLinker()->AddClassRoot(ClassRoot::OBJECT, objClass); +} + +ClassLinkerContext *EcmaClassLinkerExtension::CreateApplicationClassLinkerContext(const PandaVector &path) +{ + PandaVector app_files; + app_files.reserve(path.size()); + for (auto &p : path) { + auto pf = panda_file::OpenPandaFileOrZip(p, panda_file::File::READ_WRITE); + if (pf == nullptr) { + return nullptr; + } + app_files.push_back(std::move(pf)); + } + return ClassLinkerExtension::CreateApplicationClassLinkerContext(std::move(app_files)); +} + +Class *EcmaClassLinkerExtension::NewClass(const uint8_t *descriptor, size_t vtableSize, size_t imtSize, size_t size) +{ + ASSERT(IsInitialized()); + if (vm_ == nullptr) { + return nullptr; + } + void *ptr = vm_->GetChunk()->Allocate(coretypes::Class::GetSize(size)); + // CODECHECK-NOLINTNEXTLINE(CPP_RULE_ID_SMARTPOINTER_INSTEADOF_ORIGINPOINTER) + auto *res = reinterpret_cast(ptr); + res->InitClass(descriptor, vtableSize, imtSize, size); + res->SetClass(GetClassRoot(ClassRoot::CLASS)); + auto *klass = res->GetRuntimeClass(); + klass->SetManagedObject(res); + klass->SetSourceLang(GetLanguage()); + return klass; +} + +size_t EcmaClassLinkerExtension::GetClassSize([[maybe_unused]] ClassRoot root) +{ + ASSERT(IsInitialized()); + // Used only in test scenarios. + return sizeof(Class); +} + +size_t EcmaClassLinkerExtension::GetArrayClassSize() +{ + ASSERT(IsInitialized()); + + return GetClassSize(ClassRoot::OBJECT); +} + +void EcmaClassLinkerExtension::FreeClass([[maybe_unused]] Class *klass) +{ + ASSERT(IsInitialized()); + if (vm_ == nullptr) { + return; + } + + auto *cls = coretypes::Class::FromRuntimeClass(klass); + auto chunk = vm_->GetChunk(); + chunk->Delete(cls); +} +} // namespace panda::ecmascript diff --git a/ecmascript/ecma_class_linker_extension.h b/ecmascript/ecma_class_linker_extension.h new file mode 100644 index 0000000000000000000000000000000000000000..75c51ec0bc73e774503df012141d1a585f5eecb5 --- /dev/null +++ b/ecmascript/ecma_class_linker_extension.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMA_CLASS_LINKER_EXTENSION_H +#define PANDA_RUNTIME_ECMA_CLASS_LINKER_EXTENSION_H + +#include "libpandafile/file_items.h" +#include "include/class_linker.h" +#include "include/class_linker_extension.h" + +namespace panda::coretypes { +class Class; // NOLINT(bugprone-forward-declaration-namespace) +} // namespace panda::coretypes + +namespace panda { +namespace ecmascript { +class EcmaVM; +class EcmaClassLinkerExtension : public ClassLinkerExtension { +public: + static EcmaClassLinkerExtension *Cast(ClassLinkerExtension *object) + { + return reinterpret_cast(object); + } + + EcmaClassLinkerExtension() : ClassLinkerExtension(panda_file::SourceLang::ECMASCRIPT) {} + + ~EcmaClassLinkerExtension() override; + + NO_COPY_SEMANTIC(EcmaClassLinkerExtension); + NO_MOVE_SEMANTIC(EcmaClassLinkerExtension); + + void InitClasses(EcmaVM *vm); + + ClassLinkerContext *CreateApplicationClassLinkerContext(const PandaVector &path) override; + + bool CanThrowException([[maybe_unused]] const Method *method) const override + { + return true; + } + + void InitializeArrayClass([[maybe_unused]] Class *array_class, [[maybe_unused]] Class *componentClass) override {} + + void InitializePrimitiveClass([[maybe_unused]] Class *primitiveClass) override {} + + size_t GetClassVTableSize([[maybe_unused]] ClassRoot root) override + { + return 0; + } + + size_t GetClassIMTSize([[maybe_unused]] ClassRoot root) override + { + return 0; + } + + size_t GetClassSize([[maybe_unused]] ClassRoot root) override; + + size_t GetArrayClassVTableSize() override + { + return 0; + } + + size_t GetArrayClassIMTSize() override + { + return 0; + } + + size_t GetArrayClassSize() override; + Class *CreateClass([[maybe_unused]] const uint8_t *descriptor, [[maybe_unused]] size_t vtableSize, size_t imtSize, + [[maybe_unused]] size_t size) override + { + return NewClass(descriptor, vtableSize, imtSize, size); + } + + void InitializeClass([[maybe_unused]] Class *klass) override {} + + const void *GetNativeEntryPointFor([[maybe_unused]] Method *method) const override + { + return nullptr; + } + + ClassLinkerErrorHandler *GetErrorHandler() override + { + return nullptr; + } + + void FreeClass(Class *klass) override; + +private: + bool InitializeImpl(bool cmpStrEnabled) override; + Class *NewClass(const uint8_t *descriptor, size_t vtableSize, size_t imtSize, size_t size); + + EcmaVM *vm_{nullptr}; +}; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_ECMA_CLASS_LINKER_EXTENSION_H diff --git a/ecmascript/ecma_exceptions.cpp b/ecmascript/ecma_exceptions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30c7ae85efffeedf75184e7cbce6c8c64e60e1d7 --- /dev/null +++ b/ecmascript/ecma_exceptions.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/error_helper.h" +#include "ecmascript/ecma_exceptions.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_object.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +void SetException(JSThread *thread, JSObject *error) +{ + if (!thread->HasPendingException()) { + thread->SetException(JSTaggedValue(error)); + } +} + +void ThrowException(JSThread *thread, const char *name, const char *msg) +{ + auto jsThread = static_cast(thread); + ObjectFactory *factory = jsThread->GetEcmaVM()->GetFactory(); + if (std::strcmp(name, REFERENCE_ERROR_STRING) == 0) { + SetException(jsThread, *factory->GetJSError(base::ErrorType::REFERENCE_ERROR, msg)); + return; + } + + if (std::strcmp(name, TYPE_ERROR_STRING) == 0) { + SetException(jsThread, *factory->GetJSError(base::ErrorType::TYPE_ERROR, msg)); + return; + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/ecma_exceptions.h b/ecmascript/ecma_exceptions.h new file mode 100644 index 0000000000000000000000000000000000000000..626b45209ceba9d05c870630d07dc4e9c06f57e4 --- /dev/null +++ b/ecmascript/ecma_exceptions.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMA_EXCEPTIONS_H +#define PANDA_RUNTIME_ECMA_EXCEPTIONS_H + +/* Do not add ecma special header's here */ +#include "ecmascript/js_thread.h" + +namespace panda::ecmascript { +static constexpr const char *TYPE_ERROR_STRING = "Lecma/TypedError;"; +static constexpr const char *REFERENCE_ERROR_STRING = "Lecma/ReferenceError;"; + +void ThrowException(JSThread *thread, const char *name, const char *msg); +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMA_EXCEPTIONS_H \ No newline at end of file diff --git a/ecmascript/ecma_handle_scope-inl.h b/ecmascript/ecma_handle_scope-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..10bc552a8aa43fda33204bfc7d2cb512ec78be28 --- /dev/null +++ b/ecmascript/ecma_handle_scope-inl.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_HANDLE_SCOPE_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_HANDLE_SCOPE_INL_H + +#include "ecmascript/ecma_handle_scope.h" +#include "ecmascript/js_thread.h" + +namespace panda::ecmascript { +inline EcmaHandleScope::EcmaHandleScope(JSThread *thread) + : thread_(thread), prevNext_(thread->handleScopeStorageNext_), prevEnd_(thread->handleScopeStorageEnd_) +{ +} + +inline EcmaHandleScope::~EcmaHandleScope() +{ + thread_->handleScopeStorageNext_ = prevNext_; + if (thread_->handleScopeStorageEnd_ != prevEnd_) { + thread_->handleScopeStorageEnd_ = prevEnd_; + thread_->ShrunkHandleStorage(prevEnd_); + } +} + +uintptr_t EcmaHandleScope::NewHandle(JSThread *thread, JSTaggedType value) +{ + auto result = thread->handleScopeStorageNext_; + if (result == thread->handleScopeStorageEnd_) { + result = reinterpret_cast(thread->ExpandHandleStorage()); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + thread->handleScopeStorageNext_ = result + 1; + *result = value; + return reinterpret_cast(result); +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_HANDLE_SCOPE_INL_H diff --git a/ecmascript/ecma_handle_scope.h b/ecmascript/ecma_handle_scope.h new file mode 100644 index 0000000000000000000000000000000000000000..552da05ee10e325a0db72bf94f3f7ae37001bdcc --- /dev/null +++ b/ecmascript/ecma_handle_scope.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_HANDLE_SCOPE_H +#define PANDA_RUNTIME_ECMASCRIPT_HANDLE_SCOPE_H + +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class JSThread; + +/* + * Handles are only valid within a HandleScope. When a handle is created for an object a cell is allocated in the + * current HandleScope. + */ +class EcmaHandleScope { +public: + inline explicit EcmaHandleScope(JSThread *thread); + + inline ~EcmaHandleScope(); + + static inline uintptr_t NewHandle(JSThread *thread, JSTaggedType value); + + JSThread *GetThread() const + { + return thread_; + } + +private: + JSThread *thread_; + JSTaggedType *prevNext_; + JSTaggedType *prevEnd_; + + NO_COPY_SEMANTIC(EcmaHandleScope); + NO_MOVE_SEMANTIC(EcmaHandleScope); +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_HANDLE_SCOPE_H diff --git a/ecmascript/ecma_heap_manager_helper.h b/ecmascript/ecma_heap_manager_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..228be789457515f6c82230669c2d371bc6370fcd --- /dev/null +++ b/ecmascript/ecma_heap_manager_helper.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_HEAPMANAGER_HELPER_H +#define PANDA_RUNTIME_ECMASCRIPT_HEAPMANAGER_HELPER_H + +namespace panda::ecmascript { +class EcmaHeapManagerHelper { +public: + explicit EcmaHeapManagerHelper(panda::mem::HeapManager *heap, JSThread *thread) + : heapManager_(heap), thread_(thread) + { + } + + TaggedObject *Create(JSHClass *hclass) + { + return CreateObject(hclass, false); + } + + TaggedObject *CreateNonMovable(JSHClass *hclass) + { + return CreateObject(hclass, true); + } + + TaggedObject *CreateObject(JSHClass *hclass, size_t size, bool nonMovable) + { + ASSERT(size != 0); + TaggedObject *obj{nullptr}; + if (LIKELY(!nonMovable)) { + obj = heapManager_->AllocateObject(hclass, size, DEFAULT_ALIGNMENT, thread_); + } else { + obj = heapManager_->AllocateNonMovableObject(hclass, size, DEFAULT_ALIGNMENT, thread_); + } + return obj; + } + + TaggedObject *CreateObject(JSHClass *hclass, bool nonMovable) + { + size_t size = hclass->GetObjectSize(); + return CreateObject(hclass, size, nonMovable); + } + + TaggedObject *AllocateNonMovableObject(JSHClass *cls, size_t size) + { + return heapManager_->AllocateNonMovableObject(cls, size); + } + ~EcmaHeapManagerHelper() = default; +private: + panda::mem::HeapManager *heapManager_{nullptr}; + JSThread *thread_; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_HEAPMANAGER_HELPER_H diff --git a/ecmascript/ecma_language_context.cpp b/ecmascript/ecma_language_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a048ef4b5f821bb616a9ea71037343f1f2823885 --- /dev/null +++ b/ecmascript/ecma_language_context.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_language_context.h" + +#include "ecmascript/ecma_class_linker_extension.h" +#include "ecmascript/ecma_exceptions.h" +#include "ecmascript/js_method.h" +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "include/tooling/pt_lang_extension.h" + +namespace panda { +using ecmascript::EcmaFrameHandler; +using ecmascript::EcmaVM; +using ecmascript::JSThread; + +std::pair EcmaLanguageContext::GetCatchMethodAndOffset(Method *method, ManagedThread *thread) const +{ + Method *catchMethod = method; + uint32_t catchOffset = 0; + auto jsThread = static_cast(thread); + EcmaFrameHandler frameHandler(jsThread); + for (; frameHandler.HasFrame(); frameHandler.PrevFrame()) { + catchMethod = frameHandler.GetMethod(); + if (catchMethod->IsNative()) { + continue; + } + catchOffset = catchMethod->FindCatchBlock(jsThread->GetException().GetTaggedObject()->ClassAddr(), + frameHandler.GetBytecodeOffset()); + if (catchOffset != panda_file::INVALID_OFFSET) { + break; + } + } + + return std::make_pair(catchMethod, catchOffset); +} + +PandaVM *EcmaLanguageContext::CreateVM(Runtime *runtime, [[maybe_unused]] const RuntimeOptions &options) const +{ + auto ret = EcmaVM::Create(runtime); + if (ret) { + return ret.Value(); + } + return nullptr; +} + +std::unique_ptr EcmaLanguageContext::CreateClassLinkerExtension() const +{ + return std::make_unique(); +} + +PandaUniquePtr EcmaLanguageContext::CreatePtLangExt() const +{ + return PandaUniquePtr(); +} + +void EcmaLanguageContext::ThrowException(ManagedThread *thread, const uint8_t *mutf8_name, + const uint8_t *mutf8_msg) const +{ + ecmascript::ThrowException(JSThread::Cast(thread), reinterpret_cast(mutf8_name), + reinterpret_cast(mutf8_msg)); +} +} // namespace panda diff --git a/ecmascript/ecma_language_context.h b/ecmascript/ecma_language_context.h new file mode 100644 index 0000000000000000000000000000000000000000..ed49410f49ba4e5f0be5140a64c54dad7796e853 --- /dev/null +++ b/ecmascript/ecma_language_context.h @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_LANGUAGE_CONTEXT_H +#define PANDA_RUNTIME_ECMASCRIPT_LANGUAGE_CONTEXT_H + +#include "ecmascript/common.h" +#include "include/language_context.h" + +namespace panda { +class PUBLIC_API EcmaLanguageContext : public LanguageContextBase { +public: + EcmaLanguageContext() = default; + + DEFAULT_COPY_SEMANTIC(EcmaLanguageContext); + DEFAULT_MOVE_SEMANTIC(EcmaLanguageContext); + + ~EcmaLanguageContext() override = default; + + panda_file::SourceLang GetLanguage() const override + { + return panda_file::SourceLang::ECMASCRIPT; + } + + std::pair GetCatchMethodAndOffset(Method *method, ManagedThread *thread) const override; + + PandaVM *CreateVM(Runtime *runtime, const RuntimeOptions &options) const override; + + std::unique_ptr CreateClassLinkerExtension() const override; + + PandaUniquePtr CreatePtLangExt() const override; + + void ThrowException(ManagedThread *thread, const uint8_t *mutf8_name, const uint8_t *mutf8_msg) const override; + + coretypes::TaggedValue GetInitialTaggedValue() const override + { + UNREACHABLE(); + } + + uint64_t GetTypeTag([[maybe_unused]] interpreter::TypeTag tag) const override + { + UNREACHABLE(); + } + + DecodedTaggedValue GetInitialDecodedValue() const override + { + UNREACHABLE(); + } + + DecodedTaggedValue GetDecodedTaggedValue([[maybe_unused]] const coretypes::TaggedValue &value) const override + { + UNREACHABLE(); + } + + coretypes::TaggedValue GetEncodedTaggedValue([[maybe_unused]] int64_t value, + [[maybe_unused]] int64_t tag) const override + { + UNREACHABLE(); + } + + mem::GC *CreateGC([[maybe_unused]] mem::GCType gc_type, [[maybe_unused]] mem::ObjectAllocatorBase *object_allocator, + [[maybe_unused]] const mem::GCSettings &settings) const override + { + UNREACHABLE(); + return nullptr; + } + + void SetExceptionToVReg([[maybe_unused]] Frame::VRegister &vreg, [[maybe_unused]] ObjectHeader *obj) const override + { + UNREACHABLE(); + } + + bool IsCallableObject([[maybe_unused]] ObjectHeader *obj) const override + { + UNREACHABLE(); + } + + Method *GetCallTarget([[maybe_unused]] ObjectHeader *obj) const override + { + UNREACHABLE(); + } + + const uint8_t *GetStringClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/JSString;"); + } + + const uint8_t *GetObjectClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/JSObject;"); + } + + const uint8_t *GetClassClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/HClass;"); + } + + const uint8_t *GetClassArrayClassDescriptor() const override + { + return utf::CStringAsMutf8("[Lpanda/JSObject;"); + } + + const uint8_t *GetStringArrayClassDescriptor() const override + { + return utf::CStringAsMutf8("[Lpanda/JSString;"); + } + + const uint8_t *GetCtorName() const override + { + return utf::CStringAsMutf8(".ctor"); + } + + const uint8_t *GetCctorName() const override + { + return utf::CStringAsMutf8(".cctor"); + } + + const uint8_t *GetNullPointerExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/NullPointerException;"); + } + + const uint8_t *GetArrayIndexOutOfBoundsExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ArrayIndexOutOfBoundsException;"); + } + + const uint8_t *GetIndexOutOfBoundsExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/IndexOutOfBoundsException;"); + } + + const uint8_t *GetIllegalStateExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/IllegalStateException;"); + } + + const uint8_t *GetNegativeArraySizeExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/NegativeArraySizeException;"); + } + + const uint8_t *GetStringIndexOutOfBoundsExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/StringIndexOutOfBoundsException;"); + } + + const uint8_t *GetArithmeticExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ArithmeticException;"); + } + + const uint8_t *GetClassCastExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ClassCastException;"); + } + + const uint8_t *GetAbstractMethodErrorClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/AbstractMethodError;"); + } + + const uint8_t *GetArrayStoreExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ArrayStoreException;"); + } + + const uint8_t *GetRuntimeExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/RuntimeException;"); + } + + const uint8_t *GetFileNotFoundExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/FileNotFoundException;"); + } + + const uint8_t *GetIOExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/IOException;"); + } + + const uint8_t *GetIllegalArgumentExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/IllegalArgumentException;"); + } + + const uint8_t *GetOutOfMemoryErrorClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/OutOfMemoryError;"); + } + + const uint8_t *GetNoClassDefFoundErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/NoClassDefFoundError;"); + } + + const uint8_t *GetClassCircularityErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ClassCircularityError;"); + } + + const uint8_t *GetNoSuchFieldErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/NoSuchFieldError;"); + } + + const uint8_t *GetNoSuchMethodErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/NoSuchMethodError;"); + } + + const uint8_t *GetExceptionInInitializerErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ExceptionInInitializerError;"); + } + + const uint8_t *GetClassNotFoundExceptionDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ClassNotFoundException;"); + } + + const uint8_t *GetInstantiationErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/InstantiationError;"); + } + + const uint8_t *GetUnsupportedOperationExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/UnsupportedOperationException;"); + } + + const uint8_t *GetVerifyErrorClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/VerifyError;"); + } + + const uint8_t *GetIllegalMonitorStateExceptionDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/IllegalMonitorStateException;"); + } + + const uint8_t *GetReferenceErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lecma/ReferenceError;"); + } + + const uint8_t *GetTypedErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lecma/TypedError;"); + } +}; +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_LANGUAGE_CONTEXT_H diff --git a/ecmascript/ecma_macros.h b/ecmascript/ecma_macros.h new file mode 100644 index 0000000000000000000000000000000000000000..8de569ae14afef381de65592ffcd6cde1ded84fb --- /dev/null +++ b/ecmascript/ecma_macros.h @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_ECMA_MACROS_H +#define PANDA_RUNTIME_ECMASCRIPT_ECMA_MACROS_H + +#include "ecmascript/common.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/barriers-inl.h" +#include "ecmascript/mem/slots.h" +#include "utils/logger.h" + +#if defined(__cplusplus) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define LOG_ECMA(type) \ + LOG(type, ECMASCRIPT) << __func__ << " Line:" << __LINE__ << " " // NOLINT(bugprone-lambda-function-name) + +/* Note: We can't statically decide the element type is a primitive or heap object, especially for */ +/* dynamically-typed languages like JavaScript. So we simply skip the read-barrier. */ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GET_VALUE(addr, offset) Barriers::GetDynValue((addr), (offset)) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_VALUE_WITH_BARRIER(thread, addr, offset, value) \ + if (value.IsHeapObject()) { \ + Barriers::SetDynObject(thread, addr, offset, value.GetRawData()); \ + } else { \ + Barriers::SetDynPrimitive(addr, offset, value.GetRawData()); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_VALUE_PRIMITIVE(addr, offset, value) \ + Barriers::SetDynPrimitive(this, offset, value.GetRawData()) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ACCESSORS(name, offset, lastOffset) \ + static constexpr size_t lastOffset = offset + JSTaggedValue::TaggedTypeSize(); \ + JSTaggedValue Get##name() const \ + { \ + /* Note: We can't statically decide the element type is a primitive or heap object, especially for */ \ + /* dynamically-typed languages like JavaScript. So we simply skip the read-barrier. */ \ + return JSTaggedValue(Barriers::GetDynValue(this, offset)); \ + } \ + template \ + void Set##name(const JSThread *thread, JSHandle value, BarrierMode mode = WRITE_BARRIER) \ + { \ + if (mode == WRITE_BARRIER) { \ + if (value.GetTaggedValue().IsHeapObject()) { \ + Barriers::SetDynObject(thread, this, offset, value.GetTaggedValue().GetRawData()); \ + } else { \ + Barriers::SetDynPrimitive(this, offset, value.GetTaggedValue().GetRawData()); \ + } \ + } else { \ + Barriers::SetDynPrimitive(this, offset, value.GetTaggedValue().GetRawData()); \ + } \ + } \ + void Set##name(const JSThread *thread, JSTaggedValue value, BarrierMode mode = WRITE_BARRIER) \ + { \ + if (mode == WRITE_BARRIER) { \ + if (value.IsHeapObject()) { \ + Barriers::SetDynObject(thread, this, offset, value.GetRawData()); \ + } else { \ + Barriers::SetDynPrimitive(this, offset, value.GetRawData()); \ + } \ + } else { \ + Barriers::SetDynPrimitive(this, offset, value.GetRawData()); \ + } \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_GET_VOID_FIELD(name, offset, lastOffset) \ + static constexpr size_t lastOffset = offset + JSTaggedValue::TaggedTypeSize(); \ + void Set##name(void *fun) \ + { \ + Barriers::SetDynPrimitive(offset, fun); \ + } \ + void *Get##name() const \ + { \ + return Barriers::GetDynValue(offset); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_GET_NATIVE_FIELD(name, type, offset, lastOffset) \ + static constexpr size_t lastOffset = offset + JSTaggedValue::TaggedTypeSize(); \ + void Set##name(type *mem) \ + { \ + Barriers::SetDynPrimitive(this, offset, mem); \ + } \ + type *Get##name() const \ + { \ + return Barriers::GetDynValue(this, offset); \ + } \ + size_t Offset##name() const \ + { \ + return offset; \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_GET_PRIMITIVE_FIELD(name, type, offset, lastOffset) \ + static constexpr size_t lastOffset = offset + JSTaggedValue::TaggedTypeSize(); \ + void Set##name(type value) \ + { \ + Barriers::SetDynPrimitive(this, offset, value); \ + } \ + type Get##name() const \ + { \ + return Barriers::GetDynValue(this, offset); \ + } + +#if !defined(NDEBUG) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DASSERT(cond) assert(cond) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DASSERT_PRINT(cond, message) \ + if (auto cond_val = cond; UNLIKELY(!(cond_val))) { \ + std::cerr << message << std::endl; \ + ASSERT(#cond &&cond_val); \ + } +#else // NDEBUG +#define DASSERT(cond) static_cast(0) // NOLINT(cppcoreguidelines-macro-usage) +#define DASSERT_PRINT(cond, message) static_cast(0) // NOLINT(cppcoreguidelines-macro-usage) +#endif // !NDEBUG + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RASSERT(cond) assert(cond) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RASSERT_PRINT(cond, message) \ + if (auto cond_val = cond; UNLIKELY(!(cond_val))) { \ + std::cerr << message << std::endl; \ + RASSERT(#cond &&cond_val); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_IF_ABRUPT_COMPLETION(thread) \ + do { \ + if (thread->HasPendingException()) { \ + return; \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, value) \ + do { \ + if (thread->HasPendingException()) { \ + return (value); \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread) \ + do { \ + if (thread->HasPendingException()) { \ + return JSTaggedValue::Exception(); \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_HANDLE_IF_ABRUPT_COMPLETION(type, thread) \ + do { \ + if (thread->HasPendingException()) { \ + return JSHandle(thread, JSTaggedValue::Exception()); \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ASSERT_NO_ABRUPT_COMPLETION(thread) ASSERT(!(thread)->HasPendingException()); + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, value) \ + do { \ + if (!thread->HasPendingException()) { \ + thread->SetException(error); \ + } \ + return (value); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_NEW_ERROR_AND_RETURN_EXCEPTION(thread, error) \ + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_DATE_VALUE(name, code, isLocal) \ + static JSTaggedValue name(EcmaRuntimeCallInfo *argv) \ + { \ + ASSERT(argv); \ + JSThread *thread = argv->GetThread(); \ + JSHandle msg = GetThis(argv); \ + if (!msg->IsDate()) { \ + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); \ + } \ + JSHandle jsDate(thread, JSDate::Cast(msg->GetTaggedObject())); \ + JSTaggedValue result = jsDate->SetDateValue(argv, code, isLocal); \ + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); \ + jsDate->SetTimeValue(thread, result); \ + return result; \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DATE_TO_STRING(name) \ + static JSTaggedValue name(EcmaRuntimeCallInfo *argv) \ + { \ + ASSERT(argv); \ + JSThread *thread = argv->GetThread(); \ + JSHandle msg = GetThis(argv); \ + if (!msg->IsDate()) { \ + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); \ + } \ + if (std::isnan(JSDate::Cast(msg->GetTaggedObject())->GetTimeValue().GetDouble())) { \ + THROW_RANGE_ERROR_AND_RETURN(thread, "range error", JSTaggedValue::Exception()); \ + } \ + return JSDate::Cast(msg->GetTaggedObject())->name(thread); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DATE_STRING(name) \ + static JSTaggedValue name(EcmaRuntimeCallInfo *argv) \ + { \ + ASSERT(argv); \ + JSThread *thread = argv->GetThread(); \ + JSHandle msg = GetThis(argv); \ + if (!msg->IsDate()) { \ + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); \ + } \ + if (std::isnan(JSDate::Cast(msg->GetTaggedObject())->GetTimeValue().GetDouble())) { \ + return thread->GetEcmaVM()->GetFactory()->NewFromString("Invalid Date").GetTaggedValue(); \ + } \ + return JSDate::Cast(msg->GetTaggedObject())->name(thread); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GET_DATE_VALUE(name, code, isLocal) \ + static JSTaggedValue name(EcmaRuntimeCallInfo *argv) \ + { \ + ASSERT(argv); \ + JSThread *thread = argv->GetThread(); \ + JSHandle msg = GetThis(argv); \ + if (!msg->IsDate()) { \ + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); \ + } \ + JSHandle jsDate(thread, JSDate::Cast(msg->GetTaggedObject())); \ + double result = jsDate->GetDateValue(jsDate->GetTimeValue().GetDouble(), code, isLocal); \ + return GetTaggedDouble(result); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_NEW_ERROR_AND_RETURN(thread, error) \ + do { \ + if (!thread->HasPendingException()) { \ + thread->SetException(error); \ + } \ + return; \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_TYPE_ERROR_AND_RETURN(thread, message, exception) \ + do { \ + ObjectFactory *objectFactory = thread->GetEcmaVM()->GetFactory(); \ + JSHandle error = objectFactory->GetJSError(ErrorType::TYPE_ERROR, message); \ + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error.GetTaggedValue(), exception); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_TYPE_ERROR(thread, message) \ + do { \ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); \ + JSHandle error = factory->GetJSError(ErrorType::TYPE_ERROR, message); \ + THROW_NEW_ERROR_AND_RETURN(thread, error.GetTaggedValue()); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_RANGE_ERROR_AND_RETURN(thread, message, exception) \ + do { \ + ObjectFactory *objectFactory = thread->GetEcmaVM()->GetFactory(); \ + JSHandle error = objectFactory->GetJSError(ErrorType::RANGE_ERROR, message); \ + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error.GetTaggedValue(), exception); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_RANGE_ERROR(thread, message) \ + do { \ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); \ + JSHandle error = factory->GetJSError(ErrorType::RANGE_ERROR, message); \ + THROW_NEW_ERROR_AND_RETURN(thread, error.GetTaggedValue()); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_URI_ERROR_AND_RETURN(thread, message, exception) \ + do { \ + ObjectFactory *objectFactory = thread->GetEcmaVM()->GetFactory(); \ + JSHandle error = objectFactory->GetJSError(ErrorType::URI_ERROR, message); \ + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error.GetTaggedValue(), exception); \ + } while (false) + +#define THROW_SYNTAX_ERROR_AND_RETURN(thread, message, exception) \ + do { \ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); \ + JSHandle error = factory->GetJSError(ErrorType::SYNTAX_ERROR, message); \ + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error.GetTaggedValue(), exception); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_REJECT_PROMISE_IF_ABRUPT(thread, value, capability) \ + do { \ + ObjectFactory *objectFactory = thread->GetEcmaVM()->GetFactory(); \ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); \ + if (value.GetTaggedValue().IsCompletionRecord()) { \ + JSHandle record = JSHandle::Cast(value); \ + if (record->IsThrow()) { \ + JSHandle reject(thread, capability->GetReject()); \ + array_size_t length = 1; \ + JSHandle array = objectFactory->NewTaggedArray(length); \ + array->Set(thread, 0, record->GetValue()); \ + JSHandle undefine = globalConst->GetHandledUndefined(); \ + JSTaggedValue taggedValue = JSFunction::Call(thread, reject, undefine, array); \ + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, taggedValue); \ + return capability->GetPromise(); \ + } \ + } \ + if (thread->HasPendingException()) { \ + thread->ClearException(); \ + JSHandle reject(thread, capability->GetReject()); \ + array_size_t length = 1; \ + JSHandle array = objectFactory->NewTaggedArray(length); \ + array->Set(thread, 0, value); \ + JSHandle undefined = globalConst->GetHandledUndefined(); \ + JSTaggedValue taggedValue = JSFunction::Call(thread, reject, undefined, array); \ + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, taggedValue); \ + return capability->GetPromise(); \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_COMPLETION_IF_ABRUPT(thread, value) \ + do { \ + if (thread->HasPendingException()) { \ + JSHandle completionRecord = \ + factory->NewCompletionRecord(CompletionRecord::THROW, value); \ + return (completionRecord); \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECL_DUMP() \ + void Dump(JSThread *thread, std::ostream &os) const DUMP_API_ATTR; \ + void Dump(JSThread *thread) const DUMP_API_ATTR \ + { \ + Dump(thread, std::cout); \ + } \ + void DumpForSnapshot(JSThread *thread, std::vector> &vec) const; + +#endif // defined(__cplusplus) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECL_CAST(TYPE) \ + static TYPE *Cast(ObjectHeader *object) \ + { \ + ASSERT(JSTaggedValue(object).Is##TYPE()); \ + return reinterpret_cast(object); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECL_VISIT_OBJECT(BEGIN_OFFSET, SIZE) \ + void Visitor(const EcmaObjectRangeVisitor &visitor) \ + { \ + visitor(this, ObjectSlot(ToUintPtr(this) + BEGIN_OFFSET), ObjectSlot(ToUintPtr(this) + SIZE)); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECL_VISIT_OBJECT_FOR_JS_OBJECT(PARENTCLASS, BEGIN_OFFSET, SIZE) \ + void Visitor(const EcmaObjectRangeVisitor &visitor) \ + { \ + VisitObjects(visitor); \ + /* visit in object fields */ \ + auto objSize = this->GetClass()->GetObjectSize(); \ + if (objSize > SIZE) { \ + visitor(this, ObjectSlot(ToUintPtr(this) + SIZE), ObjectSlot(ToUintPtr(this) + objSize)); \ + } \ + } \ + void VisitObjects(const EcmaObjectRangeVisitor &visitor) \ + { \ + PARENTCLASS::VisitObjects(visitor); \ + if (BEGIN_OFFSET == SIZE) { \ + return; \ + } \ + visitor(this, ObjectSlot(ToUintPtr(this) + BEGIN_OFFSET), ObjectSlot(ToUintPtr(this) + SIZE)); \ + } + +#endif // PANDA_RUNTIME_ECMASCRIPT_ECMA_MACROS_H diff --git a/ecmascript/ecma_module.cpp b/ecmascript/ecma_module.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3dc94b52efb8cf765f30a6fdb35c131ee00c212b --- /dev/null +++ b/ecmascript/ecma_module.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_module.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array.h" +#include "ecmascript/tagged_dictionary.h" + +namespace panda::ecmascript { +static constexpr uint32_t DICTIONART_CAP = 4; +constexpr uint32_t PREVMODULE_CAP = 2; + +JSHandle EcmaModule::GetItem(const JSThread *thread, JSHandle itemName) +{ + JSHandle moduleItems(thread, NameDictionary::Cast(GetNameDictionary().GetTaggedObject())); + int entry = moduleItems->FindEntry(itemName.GetTaggedValue()); + if (entry != -1) { + return JSHandle(thread, moduleItems->GetValue(entry)); + } + + return JSHandle(thread, JSTaggedValue::Undefined()); +} + +void EcmaModule::AddItem(const JSThread *thread, JSHandle itemName, JSHandle itemValue) +{ + JSHandle data(thread, GetNameDictionary()); + if (data->IsUndefined()) { + JSHandle dict(thread, NameDictionary::Create(thread, DICTIONART_CAP)); + auto result = dict->Put(thread, dict, itemName, itemValue, PropertyAttributes::Default()); + SetNameDictionary(thread, JSTaggedValue(result)); + } else { + JSHandle dataDict = JSHandle::Cast(data); + auto result = dataDict->Put(thread, dataDict, itemName, itemValue, PropertyAttributes::Default()); + SetNameDictionary(thread, JSTaggedValue(result)); + } +} + +void EcmaModule::RemoveItem(const JSThread *thread, JSHandle itemName) +{ + JSHandle moduleItems(thread, NameDictionary::Cast(GetNameDictionary().GetTaggedObject())); + int entry = moduleItems->FindEntry(itemName.GetTaggedValue()); + if (entry != -1) { + NameDictionary::Remove(thread, moduleItems, entry); // discard return + } +} + +void EcmaModule::CopyModuleInternal(const JSThread *thread, JSHandle srcModule) +{ + JSHandle moduleItems(thread, + NameDictionary::Cast(srcModule->GetNameDictionary().GetTaggedObject())); + uint32_t numAllKeys = moduleItems->EntriesCount(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle allKeys = factory->NewTaggedArray(numAllKeys); + moduleItems->GetAllKeys(thread, 0, allKeys.GetObject()); + + JSMutableHandle itemName(thread, JSTaggedValue::Undefined()); + JSMutableHandle itemValue(thread, JSTaggedValue::Undefined()); + unsigned int capcity = allKeys->GetLength(); + for (unsigned int i = 0; i < capcity; i++) { + int entry = moduleItems->FindEntry(allKeys->Get(i)); + if (entry != -1) { + itemName.Update(allKeys->Get(i)); + itemValue.Update(moduleItems->GetValue(entry)); + AddItem(thread, itemName, itemValue); + } + } +} + +void EcmaModule::DebugPrint(const JSThread *thread, const CString &caller) +{ + JSHandle moduleItems(thread, NameDictionary::Cast(GetNameDictionary().GetTaggedObject())); + uint32_t numAllKeys = moduleItems->EntriesCount(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle allKeys = factory->NewTaggedArray(numAllKeys); + moduleItems->GetAllKeys(thread, 0, allKeys.GetObject()); + + unsigned int capcity = allKeys->GetLength(); + for (unsigned int i = 0; i < capcity; i++) { + int entry = moduleItems->FindEntry(allKeys->Get(i)); + if (entry != -1) { + std::cout << "[" << caller << "]" << std::endl + << "--itemName: " << ConvertToString(EcmaString::Cast(allKeys->Get(i).GetTaggedObject())) + << std::endl + << "--itemValue(ObjRef): 0x" << std::hex << moduleItems->GetValue(entry).GetRawData() + << std::endl; + } + } +} + +ModuleManager::ModuleManager(EcmaVM *vm) : vm_(vm) +{ + ecmaModules_ = JSTaggedValue(NameDictionary::Create(vm_->GetJSThread(), DICTIONART_CAP)); +} + +// class ModuleManager +void ModuleManager::AddModule(JSHandle moduleName, JSHandle module) +{ + [[maybe_unused]] EcmaHandleScope scope(vm_->GetJSThread()); + JSHandle dict(vm_->GetJSThread(), ecmaModules_); + ecmaModules_ = + JSTaggedValue(dict->Put(vm_->GetJSThread(), dict, moduleName, module, PropertyAttributes::Default())); +} + +void ModuleManager::RemoveModule(JSHandle moduleName) +{ + [[maybe_unused]] EcmaHandleScope scope(vm_->GetJSThread()); + JSThread *thread = vm_->GetJSThread(); + JSHandle moduleItems(thread, ecmaModules_); + int entry = moduleItems->FindEntry(moduleName.GetTaggedValue()); + if (entry != -1) { + NameDictionary::Remove(vm_->GetJSThread(), moduleItems, entry); // discard return + } +} + +JSHandle ModuleManager::GetModule(const JSThread *thread, + [[maybe_unused]] JSHandle moduleName) +{ + int entry = NameDictionary::Cast(ecmaModules_.GetTaggedObject())->FindEntry(moduleName.GetTaggedValue()); + if (entry != -1) { + return JSHandle(thread, NameDictionary::Cast(ecmaModules_.GetTaggedObject())->GetValue(entry)); + } + return thread->GlobalConstants()->GetHandledUndefined(); +} + +const CString &ModuleManager::GetCurrentExportModuleName() +{ + return moduleStack_.GetTop(); +} + +void ModuleManager::SetCurrentExportModuleName(const std::string_view &moduleFile) +{ + moduleStack_.PushModule(CString(moduleFile)); // xx/xx/x.abc +} + +void ModuleManager::RestoreCurrentExportModuleName() +{ + moduleStack_.PopModule(); +} + +const CString &ModuleManager::GetPrevExportModuleName() +{ + return moduleStack_.GetPrevModule(); +} + +void ModuleManager::AddModuleItem(const JSThread *thread, JSHandle itemName, + JSHandle value) +{ + CString name_str = GetCurrentExportModuleName(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle moduleName = factory->NewFromString(name_str); + + JSHandle module = GetModule(thread, JSHandle::Cast(moduleName)); + if (module->IsUndefined()) { + JSHandle emptyModule = factory->NewEmptyEcmaModule(); + emptyModule->AddItem(thread, itemName, value); + + AddModule(JSHandle::Cast(moduleName), JSHandle::Cast(emptyModule)); + } else { + EcmaModule::Cast(module->GetTaggedObject())->AddItem(thread, itemName, value); + } +} + +JSHandle ModuleManager::GetModuleItem(const JSThread *thread, JSHandle module, + JSHandle itemName) +{ + return EcmaModule::Cast(module->GetTaggedObject())->GetItem(thread, itemName); // Assume the module is exist +} + +void ModuleManager::CopyModule(const JSThread *thread, JSHandle src) +{ + ASSERT(src->IsHeapObject()); + JSHandle srcModule = JSHandle::Cast(src); + CString name_str = GetCurrentExportModuleName(); // Assume the srcModule exist when dstModule Execute + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle moduleName = factory->NewFromString(name_str); + + JSHandle dstModule = GetModule(thread, JSHandle::Cast(moduleName)); + + if (dstModule->IsUndefined()) { + JSHandle emptyModule = factory->NewEmptyEcmaModule(); + emptyModule->CopyModuleInternal(thread, srcModule); + + AddModule(JSHandle::Cast(moduleName), JSHandle::Cast(emptyModule)); + } else { + JSHandle::Cast(dstModule)->CopyModuleInternal(thread, srcModule); + } +} + +void ModuleManager::DebugPrint([[maybe_unused]] const JSThread *thread, [[maybe_unused]] const CString &caller) +{ + moduleStack_.DebugPrint(std::cout); +} + +// class ModuleStack +void ModuleStack::PushModule(const CString &moduleName) +{ + data_.push_back(moduleName); +} + +void ModuleStack::PopModule() +{ + data_.pop_back(); +} + +const CString &ModuleStack::GetTop() +{ + return data_.back(); +} + +const CString &ModuleStack::GetPrevModule() +{ + if (data_.size() >= PREVMODULE_CAP) { + return data_[data_.size() - PREVMODULE_CAP]; + } + + UNREACHABLE(); +} + +void ModuleStack::DebugPrint(std::ostream &dump) const +{ + dump << "ModuleStack:\n"; + if (data_.empty()) { + dump << "empty \n"; + return; + } + // NOLINTNEXTLINE(hicpp-use-auto, modernize-use-auto) + for (std::vector::const_reverse_iterator it = data_.rbegin(); it != data_.rend(); it++) { + dump << *it << "\n"; + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/ecma_module.h b/ecmascript/ecma_module.h new file mode 100644 index 0000000000000000000000000000000000000000..aa3aa2562cb43d80020efb89b62b36d24353ceb7 --- /dev/null +++ b/ecmascript/ecma_module.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_ECMA_MODULE_H +#define PANDA_RUNTIME_ECMASCRIPT_ECMA_MODULE_H + +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/mem/c_string.h" + +namespace panda::ecmascript { +class EcmaVm; + +// Forward declaration +class EcmaModule : public ECMAObject { +public: + static EcmaModule *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + JSHandle GetItem(const JSThread *thread, JSHandle itemName); + + void AddItem(const JSThread *thread, JSHandle itemName, JSHandle itemValue); + + void RemoveItem(const JSThread *thread, JSHandle itemName); + + void DebugPrint(const JSThread *thread, const CString &caller); + + static constexpr size_t NAME_DICTIONARY_OFFSET = ECMAObject::SIZE; + ACCESSORS(NameDictionary, NAME_DICTIONARY_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(ECMAObject, NAME_DICTIONARY_OFFSET, SIZE) + +protected: + void CopyModuleInternal(const JSThread *thread, JSHandle srcModule); + + friend class ModuleManager; +}; + +class ModuleStack { +public: + ModuleStack() = default; + ~ModuleStack() = default; + + NO_COPY_SEMANTIC(ModuleStack); + NO_MOVE_SEMANTIC(ModuleStack); + + void PushModule(const CString &moduleName); + void PopModule(); + const CString &GetTop(); + const CString &GetPrevModule(); + void DebugPrint(std::ostream &dump) const; + +private: + std::vector data_; +}; + +class ModuleManager { +public: + explicit ModuleManager(EcmaVM *vm); + ~ModuleManager() = default; + + void AddModule(JSHandle moduleName, JSHandle module); + + void RemoveModule(JSHandle moduleName); + + JSHandle GetModule(const JSThread *thread, JSHandle moduleName); + + const CString &GetCurrentExportModuleName(); + + const CString &GetPrevExportModuleName(); + + void SetCurrentExportModuleName(const std::string_view &moduleFile); + + void RestoreCurrentExportModuleName(); + + void AddModuleItem(const JSThread *thread, JSHandle itemName, JSHandle value); + + JSHandle GetModuleItem(const JSThread *thread, JSHandle module, + JSHandle itemName); + + void CopyModule(const JSThread *thread, JSHandle src); + + void DebugPrint(const JSThread *thread, const CString &caller); + +private: + NO_COPY_SEMANTIC(ModuleManager); + NO_MOVE_SEMANTIC(ModuleManager); + + EcmaVM *vm_{nullptr}; + JSTaggedValue ecmaModules_{JSTaggedValue::Hole()}; + ModuleStack moduleStack_; + + friend class EcmaVM; + friend class ModuleStack; +}; +} // namespace panda::ecmascript + +#endif diff --git a/ecmascript/ecma_runtime.yaml b/ecmascript/ecma_runtime.yaml new file mode 100644 index 0000000000000000000000000000000000000000..07b39ccec251905563b9fbeb85394a26db537883 --- /dev/null +++ b/ecmascript/ecma_runtime.yaml @@ -0,0 +1,1480 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +intrinsics_namespace: panda::ecmascript::intrinsics + +intrinsics: +- name: Ldnan + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldnan + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldnan + +- name: Ldinfinity + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldinfinity + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldinfinity + +- name: Ldglobalthis + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldglobalthis + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldglobalthis + +- name: Ldundefined + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldundefined + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldundefined + +- name: Ldboolean + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldboolean + static: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::Ldboolean + +- name: Ldnumber + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldnumber + static: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::Ldnumber + +- name: Ldstring + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldstring + static: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::Ldstring + +- name: Ldbigint + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldbigint + static: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::Ldbigint + +- name: Ldnull + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldnull + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldnull + +- name: Ldsymbol + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldsymbol + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldsymbol + +- name: Ldobject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldobject + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Ldobject + +- name: Ldfunction + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldfunction + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Ldfunction + +- name: Ldglobal + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldglobal + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldglobal + +- name: Ldtrue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldtrue + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldtrue + +- name: Ldfalse + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldfalse + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldfalse + +- name: Add2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: add2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Add2Dyn + +- name: Sub2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: sub2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Sub2Dyn + +- name: Mul2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: mul2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Mul2Dyn + +- name: Div2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: div2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Div2Dyn + +- name: Mod2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: mod2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Mod2Dyn + +- name: EqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: eqDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::EqDyn + +- name: NotEqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: noteqDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::NotEqDyn + +- name: LessDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: lessDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::LessDyn + +- name: LessEqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: lesseqDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::LessEqDyn + +- name: GreaterDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: greaterDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::GreaterDyn + +- name: GreaterEqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: greatereqDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::GreaterEqDyn + +- name: LdObjByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldObjByValue + static: true + exception: true + signature: + ret: any + args: [u16, any, any] + impl: panda::ecmascript::intrinsics::LdObjByValue + +- name: TryLdGlobalByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: tryLdGlobalByValue + static: true + exception: true + signature: + ret: any + args: [u16, any] + impl: panda::ecmascript::intrinsics::TryLdGlobalByValue + +- name: StObjByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: stObjByValue + static: true + exception: true + signature: + ret: void + args: [u16, any, any, acc] + impl: panda::ecmascript::intrinsics::StObjByValue + +- name: Shl2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: shl2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Shl2Dyn + +- name: Shr2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: shr2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Shr2Dyn + +- name: Ashr2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ashr2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Ashr2Dyn + +- name: And2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: and2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::And2Dyn + +- name: Or2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: or2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Or2Dyn + +- name: Xor2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: xor2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Xor2Dyn + +- name: Tonumber + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: tonumber + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::Tonumber + +- name: NegDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: negDyn + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::NegDyn + +- name: NotDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: notDyn + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::NotDyn + +- name: IncDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: incDyn + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::IncDyn + +- name: DecDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: decDyn + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::DecDyn + +- name: ThrowDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: throwDyn + static: true + exception: true + signature: + ret: void + args: [acc] + impl: panda::ecmascript::intrinsics::ThrowDyn + +- name: Delobjprop + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: delobjprop + static: true + exception: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Delobjprop + +- name: Defineglobalvar + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: defineglobalvar + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Defineglobalvar + +- name: Definelocalvar + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: definelocalvar + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Definelocalvar + +- name: Definefuncexpr + class_name: Ecmascript.Intrinsics + space: ecmascript + method_name: definefuncexpr + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Definefuncexpr + +- name: DefinefuncDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: definefuncDyn + static: true + exception: true + signature: + ret: any + args: [method_id, any] + impl: panda::ecmascript::intrinsics::DefinefuncDyn + +- name: DefineNCFuncDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: defineNCFuncDyn + static: true + exception: true + signature: + ret: any + args: [method_id, any, acc] + impl: panda::ecmascript::intrinsics::DefineNCFuncDyn + +- name: NewobjDynrange + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: newobjDynrange + static: true + exception: true + signature: + ret: any + args: [u16, any] + impl: panda::ecmascript::intrinsics::NewobjDynrange + +- name: RefeqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: refeqDyn + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::RefeqDyn + +- name: ExpDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: expDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::ExpDyn + +- name: TypeofDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: typeofDyn + static: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::TypeofDyn + +- name: Callruntimerange + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: callruntimerange + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Callruntimerange + +- name: IsinDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: isinDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::IsinDyn + +- name: InstanceofDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: instanceofDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::InstanceofDyn + +- name: StrictNotEqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: strictNotEqDyn + static: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::StrictNotEqDyn + +- name: StrictEqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: strictEqDyn + static: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::StrictEqDyn + +- name: NewobjspreadDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: newobjspreadDyn + static: true + exception: true + signature: + ret: any + args: [any, any, acc] + impl: panda::ecmascript::intrinsics::NewobjspreadDyn + +- name: CallspreadDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: callspreadDyn + static: true + exception: true + signature: + ret: any + args: [any, any, any] + impl: panda::ecmascript::intrinsics::CallspreadDyn + +- name: NewlexenvDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: newlexenvDyn + static: true + exception: true + signature: + ret: any + args: [u16] + impl: panda::ecmascript::intrinsics::NewlexenvDyn + +- name: StLexVarDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StLexVarDyn + static: true + exception: true + signature: + ret: void + args: [u16, u16, any] + impl: panda::ecmascript::intrinsics::StLexVarDyn + +- name: LdLexVarDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdLexVarDyn + static: true + exception: true + signature: + ret: any + args: [u16, u16] + impl: panda::ecmascript::intrinsics::LdLexVarDyn + +- name: LdlexenvDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldlexenvDyn + static: true + exception: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::LdlexenvDyn + +- name: PopLexenvDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: popLexenvDyn + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::PopLexenvDyn + +- name: GetUnmappedArgs + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: getUnmappedArgs + static: true + exception: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::GetUnmappedArgs + +- name: Toboolean + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: toboolean + static: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::Toboolean + +- name: GetPropIterator + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: getPropIterator + static: true + exception: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::GetPropIterator + +- name: DefineGeneratorFunc + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: defineGeneratorFunc + static: true + exception: true + signature: + ret: any + args: [method_id, any] + impl: panda::ecmascript::intrinsics::DefineGeneratorFunc + +- name: CreateIterResultObj + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createIterResultObj + static: true + exception: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::CreateIterResultObj + +- name: SuspendGenerator + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: suspendGenerator + static: true + exception: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::SuspendGenerator + +- name: ResumeGenerator + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: resumeGenerator + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::ResumeGenerator + +- name: GetResumeMode + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: getResumeMode + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::GetResumeMode + +- name: CreateGeneratorObj + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createGeneratorObj + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::CreateGeneratorObj + +- name: DefineAsyncFunc + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: defineAsyncFunc + static: true + exception: true + signature: + ret: any + args: [method_id, any] + impl: panda::ecmascript::intrinsics::DefineAsyncFunc + +- name: AsyncFunctionEnter + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: asyncFunctionEnter + static: true + exception: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::AsyncFunctionEnter + +- name: AsyncFunctionAwaitUncaught + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: asyncFunctionAwaitUncaught + static: true + exception: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::AsyncFunctionAwaitUncaught + +- name: AsyncFunctionResolve + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: asyncFunctionResolve + static: true + exception: true + signature: + ret: any + args: [any, any, any] + impl: panda::ecmascript::intrinsics::AsyncFunctionResolve + +- name: AsyncFunctionReject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: asyncFunctionReject + exception: true + static: true + signature: + ret: any + args: [any, any, any] + impl: panda::ecmascript::intrinsics::AsyncFunctionReject + +- name: ThrowUndefined + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: throwUndefined + static: true + exception: true + signature: + ret: void + args: [any] + impl: panda::ecmascript::intrinsics::ThrowUndefined + +- name: ThrowConstAssignment + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: throwConstAssignment + static: true + exception: true + signature: + ret: void + args: [any] + impl: panda::ecmascript::intrinsics::ThrowConstAssignment + +- name: ThrowUndefinedIfHole + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: throwUndefinedIfHole + static: true + exception: true + signature: + ret: void + args: [any, any] + impl: panda::ecmascript::intrinsics::ThrowUndefinedIfHole + +- name: Copyrestargs + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: copyrestargs + static: true + exception: true + signature: + ret: any + args: [u16] + impl: panda::ecmascript::intrinsics::Copyrestargs + +- name: LdHole + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldHole + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::LdHole + +- name: TryStGlobalByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: tryStGlobalByValue + exception: true + static: true + signature: + ret: void + args: [u16, any, acc] + impl: panda::ecmascript::intrinsics::TryStGlobalByValue + +- name: GetTemplateObject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: getTemplateObject + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::GetTemplateObject + +- name: TryLdGlobalByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: TryLdGlobalByName + static: true + exception: true + signature: + ret: any + args: [string_id, u16] + impl: panda::ecmascript::intrinsics::TryLdGlobalByName + +- name: TryStGlobalByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: TryStGlobalByName + static: true + exception: true + signature: + ret: void + args: [string_id, u16, acc] + impl: panda::ecmascript::intrinsics::TryStGlobalByName + +- name: LdGlobalVar + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdGlobalVar + static: true + exception: true + signature: + ret: any + args: [string_id, u16] + impl: panda::ecmascript::intrinsics::LdGlobalVar + +- name: StGlobalVar + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StGlobalVar + static: true + exception: true + signature: + ret: void + args: [string_id, u16, acc] + impl: panda::ecmascript::intrinsics::StGlobalVar + +- name: LdObjByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdObjByName + static: true + exception: true + signature: + ret: any + args: [string_id, u16, any] + impl: panda::ecmascript::intrinsics::LdObjByName + +- name: StObjByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StObjByName + static: true + exception: true + signature: + ret: void + args: [string_id, u16, any, acc] + impl: panda::ecmascript::intrinsics::StObjByName + +- name: LdObjByIndex + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdObjByIndex + static: true + exception: true + signature: + ret: any + args: [u16, any, any] + impl: panda::ecmascript::intrinsics::LdObjByIndex + +- name: StObjByIndex + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StObjByIndex + static: true + exception: true + signature: + ret: void + args: [u16, any, any, acc] + impl: panda::ecmascript::intrinsics::StObjByIndex + +- name: GetNextPropName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: getnextpropname + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::GetNextPropName + +- name: ReturnUndefined + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ReturnUndefined + static: true + signature: + ret: void + args: [] + impl: panda::ecmascript::intrinsics::ReturnUndefined + +- name: Call0Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: Call0Dyn + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::CallArg0Dyn + +- name: Call1Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: Call1Dyn + static: true + exception: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::CallArg1Dyn + +- name: Call2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: Call2Dyn + static: true + exception: true + signature: + ret: any + args: [any, any, any] + impl: panda::ecmascript::intrinsics::CallArgs2Dyn + +- name: Call3Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: Call3Dyn + static: true + exception: true + signature: + ret: any + args: [any, any, any, any] + impl: panda::ecmascript::intrinsics::CallArgs3Dyn + +- name: CalliRangeDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: CalliRangeDyn + static: true + exception: true + signature: + ret: any + args: [u16, any] + impl: panda::ecmascript::intrinsics::CalliRangeDyn + +- name: CalliThisRangeDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: CalliThisRangeDyn + static: true + exception: true + signature: + ret: any + args: [u16, any] + impl: panda::ecmascript::intrinsics::CalliThisRangeDyn + +- name: CreateEmptyObject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createemptyobject + static: true + exception: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::CreateEmptyObject + +- name: CreateObjectWithBuffer + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createobjectwithbuffer + static: true + exception: true + signature: + ret: any + args: [u16] + impl: panda::ecmascript::intrinsics::CreateObjectWithBuffer + +- name: CopyDataProperties + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: copydataproperties + static: true + exception: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::CopyDataProperties + +- name: DefineGetterSetterByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: definegettersetterbyvalue + static: true + exception: true + signature: + ret: any + args: [any, any, any, any, acc] + impl: panda::ecmascript::intrinsics::DefineGetterSetterByValue + +- name: CreateEmptyArray + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createemptyarray + static: true + exception: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::CreateEmptyArray + +- name: CreateArrayWithBuffer + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createarraywithbuffer + static: true + exception: true + signature: + ret: any + args: [u16] + impl: panda::ecmascript::intrinsics::CreateArrayWithBuffer + +- name: StOwnByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StOwnByName + static: true + exception: true + signature: + ret: void + args: [string_id, u16, any, acc] + impl: panda::ecmascript::intrinsics::StOwnByName + +- name: StOwnByIndex + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StOwnByIndex + static: true + exception: true + signature: + ret: void + args: [u16, any, any, acc] + impl: panda::ecmascript::intrinsics::StOwnByIndex + +- name: StOwnByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StOwnByValue + static: true + exception: true + signature: + ret: void + args: [u16, any, any, acc] + impl: panda::ecmascript::intrinsics::StOwnByValue + +- name: StArraySpread + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: starrayspread + static: true + exception: true + signature: + ret: any + args: [any, any, acc] + impl: panda::ecmascript::intrinsics::StArraySpread + +- name: GetIterator + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: GetIterator + static: true + exception: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::GetIterator + +- name: ThrowIfNotObject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ThrowIfNotObject + static: true + exception: true + signature: + ret: void + args: [any] + impl: panda::ecmascript::intrinsics::ThrowIfNotObject + +- name: ThrowThrowNotExists + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ThrowThrowNotExists + static: true + exception: true + signature: + ret: void + args: [] + impl: panda::ecmascript::intrinsics::ThrowThrowNotExists + +- name: CreateObjectWithExcludedKeys + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: CreateObjectWithExcludedKeys + static: true + signature: + ret: any + args: [u16, any, any] + impl: panda::ecmascript::intrinsics::CreateObjectWithExcludedKeys + +- name: ThrowPatternNonCoercible + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ThrowPatternNonCoercible + static: true + exception: true + signature: + ret: void + args: [] + impl: panda::ecmascript::intrinsics::ThrowPatternNonCoercible + +- name: IterNext + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: IterNext + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::IterNext + +- name: GetIteratorNext + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: GetIteratorNext + static: true + exception: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::GetIteratorNext + +- name: CloseIterator + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: CloseIterator + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::CloseIterator + +- name: ImportModule + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ImportModule + static: true + exception: true + signature: + ret: any + args: [string_id] + impl: panda::ecmascript::intrinsics::ImportModule + +- name: StModuleVar + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StModuleVar + static: true + exception: true + signature: + ret: void + args: [string_id, acc] + impl: panda::ecmascript::intrinsics::StModuleVar + +- name: CopyModule + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: CopyModule + static: true + exception: true + signature: + ret: void + args: [any] + impl: panda::ecmascript::intrinsics::CopyModule + +- name: LdModvarByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdModvarByName + static: true + exception: true + signature: + ret: any + args: [string_id, u16, any] + impl: panda::ecmascript::intrinsics::LdModvarByName + +- name: DefineClassWithBuffer + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: DefineClassWithBuffer + static: true + exception: true + signature: + ret: any + args: [method_id, u16, any, any] + impl: panda::ecmascript::intrinsics::DefineClassWithBuffer + +- name: SuperCall + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: SuperCall + static: true + exception: true + signature: + ret: any + args: [u16, any, acc] + impl: panda::ecmascript::intrinsics::SuperCall + +- name: SuperCallSpread + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: SuperCallSpread + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::SuperCallSpread + +- name: DefineMethod + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: defineMethod + static: true + exception: true + signature: + ret: any + args: [method_id, any, acc] + impl: panda::ecmascript::intrinsics::DefineMethod + +- name: LdSuperByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdSuperByName + static: true + exception: true + signature: + ret: any + args: [string_id, u16, any] + impl: panda::ecmascript::intrinsics::LdSuperByName + +- name: StSuperByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StSuperByName + static: true + exception: true + signature: + ret: void + args: [string_id, u16, any, acc] + impl: panda::ecmascript::intrinsics::StSuperByName + +- name: StSuperByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StSuperByValue + static: true + exception: true + signature: + ret: void + args: [u16, any, any, acc] + impl: panda::ecmascript::intrinsics::StSuperByValue + +- name: LdSuperByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdSuperByValue + static: true + exception: true + signature: + ret: any + args: [u16, any, any] + impl: panda::ecmascript::intrinsics::LdSuperByValue + +- name: CreateObjectHavingMethod + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createobjecthavingmethod + static: true + exception: true + signature: + ret: any + args: [u16, acc] + impl: panda::ecmascript::intrinsics::CreateObjectHavingMethod + +- name: ThrowIfSuperNotCorrectCall + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ThrowIfSuperNotCorrectCall + static: true + signature: + ret: void + args: [u16, acc] + impl: panda::ecmascript::intrinsics::ThrowIfSuperNotCorrectCall + +- name: LdHomeObject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdHomeObject + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::LdHomeObject + +- name: SetObjectWithProto + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: setobjectwithproto + static: true + exception: true + signature: + ret: void + args: [any, any] + impl: panda::ecmascript::intrinsics::SetObjectWithProto + +- name: ThrowDeleteSuperProperty + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ThrowDeleteSuperProperty + static: true + exception: true + signature: + ret: void + args: [] + impl: panda::ecmascript::intrinsics::ThrowDeleteSuperProperty + +- name: Debugger + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: debugger + static: true + signature: + ret: void + args: [] + impl: panda::ecmascript::intrinsics::Debugger diff --git a/ecmascript/ecma_runtime_call_info.h b/ecmascript/ecma_runtime_call_info.h new file mode 100644 index 0000000000000000000000000000000000000000..ff24b99ddf09fa80760d6d1993eb568570ccd667 --- /dev/null +++ b/ecmascript/ecma_runtime_call_info.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_ECMASCRIPT_ECMA_RUNTIM_CALL_INFO_H +#define PANDA_ECMASCRIPT_ECMA_RUNTIM_CALL_INFO_H + +#include + +#include "ecmascript/common.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "js_handle.h" + +namespace panda::ecmascript { +class EcmaRuntimeCallInfo; +using EcmaEntrypoint = JSTaggedValue (*)(EcmaRuntimeCallInfo *); + +class EcmaRuntimeCallInfo { +public: + // For builtins interpreter call + EcmaRuntimeCallInfo(JSThread *thread, uint32_t numArgs, JSTaggedValue *args) + : thread_(thread), numArgs_(numArgs), gprArgs_(args, numArgs), stackArgs_(nullptr, static_cast(0)) + { + ASSERT(numArgs_ >= NUM_MANDATORY_JSFUNC_ARGS); + } + + ~EcmaRuntimeCallInfo() = default; + + inline JSThread *GetThread() const + { + return thread_; + } + + inline void SetNewTarget(JSTaggedValue tagged, [[maybe_unused]] int64_t tag = 0) + { + SetArg(NEW_TARGET_INDEX, tagged); + } + + inline void SetFunction(JSTaggedValue tagged, [[maybe_unused]] int64_t tag = 0) + { + SetArg(FUNC_INDEX, tagged); + } + + inline void SetThis(JSTaggedValue tagged, [[maybe_unused]] int64_t tag = 0) + { + SetArg(THIS_INDEX, tagged); + } + + inline void SetCallArg(uint32_t idx, JSTaggedValue tagged, [[maybe_unused]] int64_t tag = 0) + { + ASSERT_PRINT(idx < GetArgsNumber(), "Can not set values out of index range"); + SetArg(idx + FIRST_ARGS_INDEX, tagged); + } + + inline JSHandle GetArg(uint32_t idx) const + { + return JSHandle(GetArgAddress(idx)); + } + + inline JSHandle GetFunction() const + { + return GetArg(FUNC_INDEX); + } + + inline JSHandle GetNewTarget() const + { + return GetArg(NEW_TARGET_INDEX); + } + + inline JSHandle GetThis() const + { + return GetArg(THIS_INDEX); + } + + inline JSHandle GetCallArg(uint32_t idx) const + { + return GetArg(idx + FIRST_ARGS_INDEX); + } + + /* + * The number of arguments pairs excluding the 'func', 'new.target' and 'this'. For instance: + * for code fragment: " foo(v1); ", GetArgsNumber() returns 1 + */ + inline array_size_t GetArgsNumber() const + { + return numArgs_ - NUM_MANDATORY_JSFUNC_ARGS; + } + + inline uintptr_t GetArgAddress(uint32_t idx) const + { + if (idx < gprArgs_.size()) { + return reinterpret_cast(&gprArgs_[idx]); + } + return reinterpret_cast(&stackArgs_[idx - gprArgs_.size()]); + } + +private: + DEFAULT_COPY_SEMANTIC(EcmaRuntimeCallInfo); + DEFAULT_MOVE_SEMANTIC(EcmaRuntimeCallInfo); + + enum ArgsIndex : uint8_t { FUNC_INDEX = 0, NEW_TARGET_INDEX, THIS_INDEX, FIRST_ARGS_INDEX }; + + inline void SetArg(uint32_t idx, JSTaggedValue tagged) + { + *reinterpret_cast(GetArgAddress(idx)) = tagged; + } + +private: + JSThread *thread_; + uint32_t numArgs_; + Span gprArgs_; + Span stackArgs_; +}; +} // namespace panda::ecmascript + +#endif // PANDA_ECMASCRIPT_ECMA_RUNTIM_CALL_INFO_H \ No newline at end of file diff --git a/ecmascript/ecma_string-inl.h b/ecmascript/ecma_string-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..19afd4cf02d1600762aa1c09addc2e9c5ba9f651 --- /dev/null +++ b/ecmascript/ecma_string-inl.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_STRING_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_STRING_INL_H + +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/object_factory-inl.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value-inl.h" + +namespace panda::ecmascript { +/* static */ +inline EcmaString *EcmaString::Cast(ObjectHeader *object) +{ + ASSERT(JSTaggedValue(object).IsString()); + return static_cast(object); +} + +/* static */ +inline const EcmaString *EcmaString::ConstCast(const TaggedObject *object) +{ + ASSERT(JSTaggedValue(object).IsString()); + return static_cast(object); +} + +/* static */ +inline EcmaString *EcmaString::CreateEmptyString(const EcmaVM *vm) +{ + auto string = vm->GetFactory()->AllocNonMovableStringObject(sizeof(EcmaString)); + string->SetLength(0, GetCompressedStringsEnabled()); + string->SetHashcode(0); + return string; +} + +/* static */ +inline EcmaString *EcmaString::CreateFromUtf8(const uint8_t *utf8Data, uint32_t utf8Len, const EcmaVM *vm) +{ + if (utf8Len == 0) { + return vm->GetFactory()->GetEmptyString().GetObject(); + } + bool canBeCompressed = CanBeCompressed(utf8Data); + EcmaString *string = nullptr; + if (canBeCompressed) { + string = AllocStringObject(utf8Len, true, vm); + ASSERT(string != nullptr); + + if (memcpy_s(string->GetDataUtf8Writable(), utf8Len, utf8Data, utf8Len) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } else { + auto utf16Len = base::utf_helper::Utf8ToUtf16Size(utf8Data); + string = AllocStringObject(utf16Len, false, vm); + ASSERT(string != nullptr); + + [[maybe_unused]] auto len = + base::utf_helper::ConvertRegionUtf8ToUtf16(utf8Data, string->GetDataUtf16Writable(), utf16Len, 0); + ASSERT(len == utf16Len); + } + + return string; +} + +inline EcmaString *EcmaString::CreateFromUtf16(const uint16_t *utf16Data, uint32_t utf16Len, const EcmaVM *vm) +{ + if (utf16Len == 0) { + return vm->GetFactory()->GetEmptyString().GetObject(); + } + bool canBeCompressed = CanBeCompressed(utf16Data, utf16Len); + auto string = AllocStringObject(utf16Len, canBeCompressed, vm); + ASSERT(string != nullptr); + + if (canBeCompressed) { + CopyUtf16AsUtf8(utf16Data, string->GetDataUtf8Writable(), utf16Len); + } else { + uint32_t len = utf16Len * (sizeof(uint16_t) / sizeof(uint8_t)); + if (memcpy_s(string->GetDataUtf16Writable(), len, utf16Data, len) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } + + return string; +} + +template +inline uint16_t EcmaString::At(int32_t index) const +{ + int32_t length = GetLength(); + if (verify) { + if ((index < 0) || (index >= length)) { + return 0; + } + } + if (!IsUtf16()) { + Span sp(GetDataUtf8(), length); + return sp[index]; + } + Span sp(GetDataUtf16(), length); + return sp[index]; +} + +/* static */ +inline EcmaString *EcmaString::AllocStringObject(size_t length, bool compressed, const EcmaVM *vm) +{ + size_t size = compressed ? ComputeSizeUtf8(length) : ComputeSizeUtf16(length); + auto string = reinterpret_cast(vm->GetFactory()->AllocStringObject(size)); + string->SetLength(length, compressed); + string->SetHashcode(0); + return reinterpret_cast(string); +} +void EcmaString::WriteData(EcmaString *src, uint32_t start, uint32_t destSize, uint32_t length) +{ + if (IsUtf8()) { + ASSERT(src->IsUtf8()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (length != 0 && memcpy_s(GetDataUtf8Writable() + start, destSize, src->GetDataUtf8(), length) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } else if (src->IsUtf8()) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + Span to(GetDataUtf16Writable() + start, length); + Span from(src->GetDataUtf8(), length); + for (uint32_t i = 0; i < length; i++) { + to[i] = from[i]; + } + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (length != 0 && memcpy_s(GetDataUtf16Writable() + start, destSize, src->GetDataUtf16(), length) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } +} +void EcmaString::WriteData(char src, uint32_t start) +{ + if (IsUtf8()) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *(GetDataUtf8Writable() + start) = src; + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *(GetDataUtf16Writable() + start) = src; + } +} +} // namespace panda::ecmascript + +#endif diff --git a/ecmascript/ecma_string.cpp b/ecmascript/ecma_string.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d7cf38ba79bf4e887a207712f5851f5fbc24868 --- /dev/null +++ b/ecmascript/ecma_string.cpp @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string-inl.h" + +#include "ecmascript/js_symbol.h" +#include "ecmascript/mem/c_containers.h" + +namespace panda::ecmascript { +bool EcmaString::compressedStringsEnabled = true; +static constexpr int SMALL_STRING_SIZE = 128; + +EcmaString *EcmaString::Concat(const JSHandle &str1Handle, const JSHandle &str2Handle, + const EcmaVM *vm) +{ + // allocator may trig gc and move src, need to hold it + EcmaString *string1 = *str1Handle; + EcmaString *string2 = *str2Handle; + + uint32_t length1 = string1->GetLength(); + + uint32_t length2 = string2->GetLength(); + uint32_t newLength = length1 + length2; + if (newLength == 0) { + return vm->GetFactory()->GetEmptyString().GetObject(); + } + bool compressed = GetCompressedStringsEnabled() && (!string1->IsUtf16() && !string2->IsUtf16()); + auto newString = AllocStringObject(newLength, compressed, vm); + + // retrieve strings after gc + string1 = *str1Handle; + string2 = *str2Handle; + if (compressed) { + Span sp(newString->GetDataUtf8Writable(), newLength); + Span src1(string1->GetDataUtf8(), length1); + EcmaString::StringCopy(sp, newLength, src1, length1); + + sp = sp.SubSpan(length1); + Span src2(string2->GetDataUtf8(), length2); + EcmaString::StringCopy(sp, newLength - length1, src2, length2); + } else { + Span sp(newString->GetDataUtf16Writable(), newLength); + if (!string1->IsUtf16()) { + for (uint32_t i = 0; i < length1; ++i) { + sp[i] = string1->At(i); + } + } else { + Span src1(string1->GetDataUtf16(), length1); + EcmaString::StringCopy(sp, newLength << 1U, src1, length1 << 1U); + } + sp = sp.SubSpan(length1); + if (!string2->IsUtf16()) { + for (uint32_t i = 0; i < length2; ++i) { + sp[i] = string2->At(i); + } + } else { + uint32_t length = length2 << 1U; + Span src2(string2->GetDataUtf16(), length2); + EcmaString::StringCopy(sp, length, src2, length); + } + } + + return newString; +} + +/* static */ +EcmaString *EcmaString::FastSubString(const JSHandle &src, uint32_t start, uint32_t utf16Len, + const EcmaVM *vm) +{ + if (utf16Len == 0) { + return vm->GetFactory()->GetEmptyString().GetObject(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + bool canBeCompressed = !src->IsUtf16() || CanBeCompressed(src->GetDataUtf16() + start, utf16Len); + + // allocator may trig gc and move src, need to hold it + auto string = AllocStringObject(utf16Len, canBeCompressed, vm); + + if (src->IsUtf16()) { + if (canBeCompressed) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + CopyUtf16AsUtf8(src->GetDataUtf16() + start, string->GetDataUtf8Writable(), utf16Len); + } else { + uint32_t len = utf16Len * (sizeof(uint16_t) / sizeof(uint8_t)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + Span dst(string->GetDataUtf16Writable(), utf16Len); + Span source(src->GetDataUtf16() + start, utf16Len); + EcmaString::StringCopy(dst, len, source, len); + } + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + Span dst(string->GetDataUtf8Writable(), utf16Len); + Span source(src->GetDataUtf8() + start, utf16Len); + EcmaString::StringCopy(dst, utf16Len, source, utf16Len); + } + return string; +} + +template +int32_t CompareStringSpan(Span &lhsSp, Span &rhsSp, int32_t count) +{ + for (int32_t i = 0; i < count; ++i) { + auto left = static_cast(lhsSp[i]); + auto right = static_cast(rhsSp[i]); + if (left != right) { + return left - right; + } + } + return 0; +} + +int32_t EcmaString::Compare(const EcmaString *rhs) const +{ + const EcmaString *lhs = this; + if (lhs == rhs) { + return 0; + } + int32_t lhsCount = lhs->GetLength(); + int32_t rhsCount = rhs->GetLength(); + int32_t countDiff = lhsCount - rhsCount; + int32_t minCount = (countDiff < 0) ? lhsCount : rhsCount; + if (!lhs->IsUtf16() && !rhs->IsUtf16()) { + Span lhsSp(lhs->GetDataUtf8(), lhsCount); + Span rhsSp(rhs->GetDataUtf8(), rhsCount); + int32_t charDiff = CompareStringSpan(lhsSp, rhsSp, minCount); + if (charDiff != 0) { + return charDiff; + } + } else if (!lhs->IsUtf16()) { + Span lhsSp(lhs->GetDataUtf8(), lhsCount); + Span rhsSp(rhs->GetDataUtf16(), rhsCount); + int32_t charDiff = CompareStringSpan(lhsSp, rhsSp, minCount); + if (charDiff != 0) { + return charDiff; + } + } else if (!rhs->IsUtf16()) { + Span lhsSp(lhs->GetDataUtf16(), rhsCount); + Span rhsSp(rhs->GetDataUtf8(), lhsCount); + int32_t charDiff = CompareStringSpan(lhsSp, rhsSp, minCount); + if (charDiff != 0) { + return charDiff; + } + } else { + Span lhsSp(lhs->GetDataUtf16(), lhsCount); + Span rhsSp(rhs->GetDataUtf16(), rhsCount); + int32_t charDiff = CompareStringSpan(lhsSp, rhsSp, minCount); + if (charDiff != 0) { + return charDiff; + } + } + return countDiff; +} + +/* static */ +template +int32_t EcmaString::IndexOf(Span &lhsSp, Span &rhsSp, int32_t pos, int32_t max) +{ + ASSERT(rhsSp.size() > 0); + auto first = static_cast(rhsSp[0]); + int32_t i; + for (i = pos; i <= max; i++) { + if (static_cast(lhsSp[i]) != first) { + while (++i <= max && static_cast(lhsSp[i]) != first) { + } + } + /* Found first character, now look at the rest of rhsSp */ + if (i <= max) { + int j = i + 1; + int end = j + rhsSp.size() - 1; + + for (int k = 1; j < end && static_cast(lhsSp[j]) == static_cast(rhsSp[k]); j++, k++) { + } + if (j == end) { + /* Found whole string. */ + return i; + } + } + } + return -1; +} + +int32_t EcmaString::IndexOf(const EcmaString *rhs, int32_t pos) const +{ + if (rhs == nullptr) { + return -1; + } + const EcmaString *lhs = this; + int32_t lhsCount = lhs->GetLength(); + int32_t rhsCount = rhs->GetLength(); + if (rhsCount == 0) { + return pos; + } + + if (pos >= lhsCount) { + return -1; + } + + if (pos < 0) { + pos = 0; + } + + int32_t max = lhsCount - rhsCount; + if (rhs->IsUtf8() && lhs->IsUtf8()) { + Span lhsSp(lhs->GetDataUtf8(), lhsCount); + Span rhsSp(rhs->GetDataUtf8(), rhsCount); + return EcmaString::IndexOf(lhsSp, rhsSp, pos, max); + } else if (rhs->IsUtf16() && lhs->IsUtf16()) { // NOLINT(readability-else-after-return) + Span lhsSp(lhs->GetDataUtf16(), lhsCount); + Span rhsSp(rhs->GetDataUtf16(), rhsCount); + return EcmaString::IndexOf(lhsSp, rhsSp, pos, max); + } else if (rhs->IsUtf16()) { + Span lhsSp(lhs->GetDataUtf8(), lhsCount); + Span rhsSp(rhs->GetDataUtf16(), rhsCount); + return EcmaString::IndexOf(lhsSp, rhsSp, pos, max); + } else { // NOLINT(readability-else-after-return) + Span lhsSp(lhs->GetDataUtf16(), lhsCount); + Span rhsSp(rhs->GetDataUtf8(), rhsCount); + return EcmaString::IndexOf(lhsSp, rhsSp, pos, max); + } + + return -1; +} + +// static +bool EcmaString::CanBeCompressed(const uint8_t *utf8Data) +{ + if (!compressedStringsEnabled) { + return false; + } + bool isCompressed = true; + int index = 0; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + while (utf8Data[index] != '\0') { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (!IsASCIICharacter(utf8Data[index])) { + isCompressed = false; + break; + } + ++index; + } + return isCompressed; +} + +/* static */ +bool EcmaString::CanBeCompressed(const uint16_t *utf16Data, uint32_t utf16Len) +{ + if (!compressedStringsEnabled) { + return false; + } + bool isCompressed = true; + Span data(utf16Data, utf16Len); + for (uint32_t i = 0; i < utf16Len; i++) { + if (!IsASCIICharacter(data[i])) { + isCompressed = false; + break; + } + } + return isCompressed; +} + +/* static */ +void EcmaString::CopyUtf16AsUtf8(const uint16_t *utf16From, uint8_t *utf8To, uint32_t utf16Len) +{ + Span from(utf16From, utf16Len); + Span to(utf8To, utf16Len); + for (uint32_t i = 0; i < utf16Len; i++) { + to[i] = from[i]; + } +} + +/* static */ +bool EcmaString::StringsAreEqual(EcmaString *str1, EcmaString *str2) +{ + if ((str1->IsUtf16() != str2->IsUtf16()) || (str1->GetLength() != str2->GetLength()) || + (str1->GetHashcode() != str2->GetHashcode())) { + return false; + } + + if (str1->IsUtf16()) { + Span data1(str1->GetDataUtf16(), str1->GetLength()); + Span data2(str2->GetDataUtf16(), str1->GetLength()); + return EcmaString::StringsAreEquals(data1, data2); + } else { // NOLINT(readability-else-after-return) + Span data1(str1->GetDataUtf8(), str1->GetLength()); + Span data2(str2->GetDataUtf8(), str1->GetLength()); + return EcmaString::StringsAreEquals(data1, data2); + } +} + +/* static */ +bool EcmaString::StringsAreEqualUtf8(const EcmaString *str1, const uint8_t *utf8Data, uint32_t utf8Len) +{ + bool str1CanBeCompressed = str1->IsUtf8(); + if (str1CanBeCompressed && str1->GetLength() != utf8Len) { + return false; + } + + bool str2CanBeCompressed = EcmaString::CanBeCompressed(utf8Data); + if (str1CanBeCompressed != str2CanBeCompressed) { + return false; + } + + if (str1CanBeCompressed) { + Span data1(str1->GetDataUtf8(), str1->GetLength()); + Span data2(utf8Data, utf8Len); + return EcmaString::StringsAreEquals(data1, data2); + } + + return IsUtf8EqualsUtf16(utf8Data, str1->GetDataUtf16(), str1->GetLength()); +} + +/* static */ +bool EcmaString::StringsAreEqualUtf16(const EcmaString *str1, const uint16_t *utf16Data, uint32_t utf16Len) +{ + bool result = false; + if (str1->GetLength() != utf16Len) { + result = false; + } else if (!str1->IsUtf16()) { + result = IsUtf8EqualsUtf16(str1->GetDataUtf8(), utf16Data, utf16Len); + } else { + Span data1(str1->GetDataUtf16(), str1->GetLength()); + Span data2(utf16Data, utf16Len); + result = EcmaString::StringsAreEquals(data1, data2); + } + return result; +} + +/* static */ +template +bool EcmaString::StringsAreEquals(Span &str1, Span &str2) +{ + ASSERT(str1.Size() <= str2.Size()); + size_t size = str1.Size(); + if (size < SMALL_STRING_SIZE) { + for (size_t i = 0; i < size; i++) { + if (str1[i] != str2[i]) { + return false; + } + } + return true; + } + return !memcmp(str1.data(), str2.data(), size); +} + +template +bool EcmaString::StringCopy(Span &dst, size_t dstMax, Span &src, size_t count) +{ + ASSERT(dstMax >= count); + ASSERT(dst.Size() >= src.Size()); + if (src.Size() < SMALL_STRING_SIZE) { + for (size_t i = 0; i < src.Size(); i++) { + dst[i] = src[i]; + } + return true; + } + if (memcpy_s(dst.data(), dstMax, src.data(), count) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + return true; +} + +template +static int32_t ComputeHashForData(const T *data, size_t size) +{ + uint32_t hash = 0; +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-attributes" + Span sp(data, size); +#pragma GCC diagnostic pop +#endif + for (auto c : sp) { + constexpr size_t SHIFT = 5; + hash = (hash << SHIFT) - hash + c; + } + return static_cast(hash); +} + +static int32_t ComputeHashForUtf8(const uint8_t *utf8Data) +{ + if (utf8Data == nullptr) { + return 0; + } + uint32_t hash = 0; + while (*utf8Data != '\0') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + constexpr size_t SHIFT = 5; + hash = (hash << SHIFT) - hash + *utf8Data++; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + return static_cast(hash); +} + +uint32_t EcmaString::ComputeHashcode() const +{ + uint32_t hash; + if (compressedStringsEnabled) { + if (!IsUtf16()) { + hash = ComputeHashForData(GetDataUtf8(), GetLength()); + } else { + hash = ComputeHashForData(GetDataUtf16(), GetLength()); + } + } else { + ASSERT(static_cast(GetLength()) > (std::numeric_limits::max() >> 1U)); + hash = ComputeHashForData(GetDataUtf16(), GetLength()); + } + return hash; +} + +/* static */ +uint32_t EcmaString::ComputeHashcodeUtf8(const uint8_t *utf8Data) +{ + bool canBeCompressed = EcmaString::CanBeCompressed(utf8Data); + uint32_t hash; + if (canBeCompressed) { + hash = ComputeHashForUtf8(utf8Data); + } else { + auto utf16Len = base::utf_helper::Utf8ToUtf16Size(utf8Data); + CVector tmpBuffer(utf16Len); + [[maybe_unused]] auto len = base::utf_helper::ConvertRegionUtf8ToUtf16(utf8Data, tmpBuffer.data(), utf16Len, 0); + ASSERT(len == utf16Len); + hash = ComputeHashForData(tmpBuffer.data(), utf16Len); + } + return hash; +} + +/* static */ +uint32_t EcmaString::ComputeHashcodeUtf16(const uint16_t *utf16Data, uint32_t length) +{ + return ComputeHashForData(utf16Data, length); +} + +/* static */ +bool EcmaString::IsUtf8EqualsUtf16(const uint8_t *utf8Data, const uint16_t *utf16Data, uint32_t utf16Len) +{ + // length is one more than compared utf16Data, don't need convert all utf8Data to utf16Data + uint32_t utf8ConvertLength = utf16Len + 1; + CVector tmpBuffer(utf8ConvertLength); + auto len = base::utf_helper::ConvertRegionUtf8ToUtf16(utf8Data, tmpBuffer.data(), utf8ConvertLength, 0); + if (len != utf16Len) { + return false; + } + + Span data1(tmpBuffer.data(), len); + Span data2(utf16Data, utf16Len); + return EcmaString::StringsAreEquals(data1, data2); +} +} // namespace panda::ecmascript diff --git a/ecmascript/ecma_string.h b/ecmascript/ecma_string.h new file mode 100644 index 0000000000000000000000000000000000000000..d8c22d3c2aae24796fc813c21ac10a5da7c57a68 --- /dev/null +++ b/ecmascript/ecma_string.h @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_STRING_H +#define PANDA_RUNTIME_ECMASCRIPT_STRING_H + +#include +#include +#include + +#include "ecmascript/base/utf_helper.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/mem/tagged_object.h" + +namespace panda { +namespace ecmascript { +template +class JSHandle; +class EcmaVM; + +class EcmaString : public TaggedObject { +public: + static EcmaString *Cast(ObjectHeader *object); + static const EcmaString *ConstCast(const TaggedObject *object); + + static EcmaString *CreateEmptyString(const EcmaVM *vm); + static EcmaString *CreateFromUtf8(const uint8_t *utf8Data, uint32_t utf8Len, const EcmaVM *vm); + static EcmaString *CreateFromUtf16(const uint16_t *utf16Data, uint32_t utf16Len, const EcmaVM *vm); + static EcmaString *Concat(const JSHandle &str1Handle, const JSHandle &str2Handle, + const EcmaVM *vm); + static EcmaString *FastSubString(const JSHandle &src, uint32_t start, uint32_t utf16Len, + const EcmaVM *vm); + + template + uint16_t At(int32_t index) const; + + int32_t Compare(const EcmaString *rhs) const; + + bool IsUtf16() const + { + return compressedStringsEnabled ? ((length_ & STRING_COMPRESSED_BIT) == STRING_UNCOMPRESSED) : true; + } + + bool IsUtf8() const + { + return compressedStringsEnabled ? ((length_ & STRING_COMPRESSED_BIT) == STRING_COMPRESSED) : false; + } + + static size_t ComputeDataSizeUtf16(uint32_t length) + { + return length * sizeof(dataUtf16_[0]); + } + + /** + * Methods for uncompressed strings (UTF16): + */ + static size_t ComputeSizeUtf16(uint32_t utf16Len) + { + return sizeof(EcmaString) + ComputeDataSizeUtf16(utf16Len); + } + + const uint16_t *GetDataUtf16() const + { + LOG_IF(!IsUtf16(), FATAL, RUNTIME) << "EcmaString: Read data as utf16 for utf8 string"; + return dataUtf16_; + } + + /** + * Methods for compresses strings (UTF8 or LATIN1): + */ + static size_t ComputeSizeUtf8(uint32_t utf8Len) + { + return sizeof(EcmaString) + utf8Len; + } + + /** + * It's Utf8 format, but without 0 in the end. + */ + const uint8_t *GetDataUtf8() const + { + LOG_IF(IsUtf16(), FATAL, RUNTIME) << "EcmaString: Read data as utf8 for utf16 string"; + return reinterpret_cast(dataUtf16_); + } + + size_t GetUtf8Length() const + { + if (!IsUtf16()) { + return GetLength() + 1; // add place for zero in the end + } + return base::utf_helper::Utf16ToUtf8Size(dataUtf16_, GetLength()); + } + + size_t GetUtf16Length() const + { + return GetLength(); + } + + inline size_t CopyDataUtf8(uint8_t *buf, size_t maxLength) const + { + ASSERT(maxLength > 0); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + buf[maxLength - 1] = '\0'; + return CopyDataRegionUtf8(buf, 0, GetLength(), maxLength) + 1; // add place for zero in the end + } + + size_t CopyDataRegionUtf8(uint8_t *buf, size_t start, size_t length, size_t maxLength) const + { + if (length > maxLength) { + return 0; + } + uint32_t len = GetLength(); + if (start + length > len) { + return 0; + } + if (!IsUtf16()) { + if (length > std::numeric_limits::max() / 2 - 1) { // 2: half + LOG(FATAL, RUNTIME) << " length is higher than half of size_t::max"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(buf, maxLength, GetDataUtf8() + start, length) != EOK) { + LOG(FATAL, RUNTIME) << "memcpy_s failed"; + UNREACHABLE(); + } + return length; + } + return base::utf_helper::ConvertRegionUtf16ToUtf8(GetDataUtf16(), buf, length, maxLength - 1, start) - 1; + } + + inline uint32_t CopyDataUtf16(uint16_t *buf, uint32_t maxLength) const + { + return CopyDataRegionUtf16(buf, 0, GetLength(), maxLength); + } + + uint32_t CopyDataRegionUtf16(uint16_t *buf, uint32_t start, uint32_t length, uint32_t maxLength) const + { + if (length > maxLength) { + return 0; + } + uint32_t len = GetLength(); + if (start + length > len) { + return 0; + } + if (IsUtf16()) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(buf, ComputeDataSizeUtf16(maxLength), GetDataUtf16() + start, ComputeDataSizeUtf16(length)) != + EOK) { + LOG(FATAL, RUNTIME) << "memcpy_s failed"; + UNREACHABLE(); + } + return length; + } + return base::utf_helper::ConvertRegionUtf8ToUtf16(GetDataUtf8(), buf, maxLength, start); + } + + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + inline std::unique_ptr GetCString() + { + auto length = GetUtf8Length(); + char* buf = new char[length]; + CopyDataUtf8(reinterpret_cast(buf), length); + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + return std::unique_ptr(buf); + } + + inline void WriteData(EcmaString *src, uint32_t start, uint32_t destSize, uint32_t length); + inline void WriteData(char src, uint32_t start); + uint32_t GetLength() const + { + return length_ >> 2U; + } + + void SetIsInternString() + { + length_ |= STRING_INTERN_BIT; + } + + bool IsInternString() const + { + return (length_ & STRING_INTERN_BIT) != 0; + } + + size_t ObjectSize() const + { + uint32_t length = GetLength(); + return IsUtf16() ? ComputeSizeUtf16(length) : ComputeSizeUtf8(length); + } + + uint32_t GetHashcode() + { + if (hashcode_ == 0) { + hashcode_ = ComputeHashcode(); + } + return hashcode_; + } + + int32_t IndexOf(const EcmaString *rhs, int pos = 0) const; + + static constexpr uint32_t GetLengthOffset() + { + return MEMBER_OFFSET(EcmaString, length_); + } + + static constexpr uint32_t GetDataOffset() + { + return MEMBER_OFFSET(EcmaString, dataUtf16_); + } + + static constexpr uint32_t GetStringCompressionMask() + { + return STRING_COMPRESSED_BIT; + } + + /** + * Compares strings by bytes, It doesn't check canonical unicode equivalence. + */ + static bool StringsAreEqual(EcmaString *str1, EcmaString *str2); + /** + * Compares strings by bytes, It doesn't check canonical unicode equivalence. + */ + static bool StringsAreEqualUtf8(const EcmaString *str1, const uint8_t *utf8Data, uint32_t utf8Len); + /** + * Compares strings by bytes, It doesn't check canonical unicode equivalence. + */ + static bool StringsAreEqualUtf16(const EcmaString *str1, const uint16_t *utf16Data, uint32_t utf16Len); + static uint32_t ComputeHashcodeUtf8(const uint8_t *utf8Data); + static uint32_t ComputeHashcodeUtf16(const uint16_t *utf16Data, uint32_t length); + + static void SetCompressedStringsEnabled(bool val) + { + compressedStringsEnabled = val; + } + + static bool GetCompressedStringsEnabled() + { + return compressedStringsEnabled; + } + + static EcmaString *AllocStringObject(size_t length, bool compressed, const EcmaVM *vm); + +private: + void SetLength(uint32_t length, bool compressed = false) + { + ASSERT(length < 0x40000000U); + // Use 0u for compressed/utf8 expression + length_ = (length << 2U) | (compressed ? STRING_COMPRESSED : STRING_UNCOMPRESSED); + } + + void SetHashcode(uint32_t hashcode) + { + hashcode_ = hashcode; + } + + uint16_t *GetDataUtf16Writable() + { + LOG_IF(!IsUtf16(), FATAL, RUNTIME) << "EcmaString: Read data as utf16 for utf8 string"; + return dataUtf16_; + } + + uint8_t *GetDataUtf8Writable() + { + LOG_IF(IsUtf16(), FATAL, RUNTIME) << "EcmaString: Read data as utf8 for utf16 string"; + return reinterpret_cast(dataUtf16_); + } + + uint32_t ComputeHashcode() const; + static bool CanBeCompressed(const uint8_t *utf8Data); + static bool CanBeCompressed(const uint16_t *utf16Data, uint32_t utf16Len); + static void CopyUtf16AsUtf8(const uint16_t *utf16From, uint8_t *utf8To, uint32_t utf16Len); + + static bool compressedStringsEnabled; + static constexpr uint32_t STRING_COMPRESSED_BIT = 0x1; + static constexpr uint32_t STRING_INTERN_BIT = 0x2; + enum CompressedStatus { + STRING_COMPRESSED, + STRING_UNCOMPRESSED, + }; + + static bool IsASCIICharacter(uint16_t data) + { + // \0 is not considered ASCII in Ecma-Modified-UTF8 [only modify '\u0000'] + return data - 1U < base::utf_helper::UTF8_1B_MAX; + } + + /** + * str1 should have the same length as utf16_data. + * Converts utf8Data to utf16 and compare it with given utf16_data. + */ + static bool IsUtf8EqualsUtf16(const uint8_t *utf8Data, const uint16_t *utf16Data, uint32_t utf16Len); + + template + /** + * Check that two spans are equal. Should have the same length. + */ + static bool StringsAreEquals(Span &str1, Span &str2); + + template + /** + * Copy String from src to dst + * */ + static bool StringCopy(Span &dst, size_t dstMax, Span &src, size_t count); + + template + static int32_t IndexOf(Span &lhsSp, Span &rhsSp, int32_t pos, int32_t max); + + // In last bit of length_ we store if this string is compressed or not. + uint32_t length_; + uint32_t hashcode_; + // A pointer to the string data stored after the string header. + // Data can be stored in utf8 or utf16 form according to compressed bit. + __extension__ uint16_t dataUtf16_[0]; // NOLINT(modernize-avoid-c-arrays) +}; +} // namespace ecmascript +} // namespace panda +#endif // PANDA_RUNTIME_ECMASCRIPT_STRING_H diff --git a/ecmascript/ecma_string_table.cpp b/ecmascript/ecma_string_table.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f4524653c44fcd441371c474ed2cb6d586509478 --- /dev/null +++ b/ecmascript/ecma_string_table.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string_table.h" + +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/c_string.h" +#include "ecmascript/mem/heap_roots.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +EcmaStringTable::EcmaStringTable(const EcmaVM *vm) : vm_(vm) {} + +EcmaString *EcmaStringTable::GetString(const uint8_t *utf8Data, uint32_t utf8Len) const +{ + uint32_t hashCode = EcmaString::ComputeHashcodeUtf8(utf8Data); + for (auto it = table_.find(hashCode); it != table_.end(); it++) { + auto foundedString = it->second; + if (EcmaString::StringsAreEqualUtf8(foundedString, utf8Data, utf8Len)) { + return foundedString; + } + } + return nullptr; +} + +EcmaString *EcmaStringTable::GetString(const uint16_t *utf16Data, uint32_t utf16Len) const +{ + uint32_t hashCode = EcmaString::ComputeHashcodeUtf16(const_cast(utf16Data), utf16Len); + for (auto it = table_.find(hashCode); it != table_.end(); it++) { + auto foundedString = it->second; + if (EcmaString::StringsAreEqualUtf16(foundedString, utf16Data, utf16Len)) { + return foundedString; + } + } + return nullptr; +} + +EcmaString *EcmaStringTable::GetString(EcmaString *string) const +{ + auto hash = string->GetHashcode(); + for (auto it = table_.find(hash); it != table_.end(); it++) { + auto foundedString = it->second; + if (EcmaString::StringsAreEqual(foundedString, string)) { + return foundedString; + } + } + return nullptr; +} + +void EcmaStringTable::InternString(EcmaString *string) +{ + if (string->IsInternString()) { + return; + } + table_.insert(std::pair(string->GetHashcode(), string)); + string->SetIsInternString(); +} + +void EcmaStringTable::InternEmptyString(EcmaString *emptyStr) +{ + InternString(emptyStr); +} + +EcmaString *EcmaStringTable::GetOrInternString(const uint8_t *utf8Data, uint32_t utf8Len) +{ + EcmaString *result = GetString(utf8Data, utf8Len); + if (result != nullptr) { + return result; + } + + result = EcmaString::CreateFromUtf8(utf8Data, utf8Len, vm_); + + InternString(result); + + return result; +} + +EcmaString *EcmaStringTable::GetOrInternString(const uint16_t *utf16Data, uint32_t utf16Len) +{ + EcmaString *result = GetString(utf16Data, utf16Len); + if (result != nullptr) { + return result; + } + + result = EcmaString::CreateFromUtf16(utf16Data, utf16Len, vm_); + + InternString(result); + + return result; +} + +EcmaString *EcmaStringTable::GetOrInternString(EcmaString *string) +{ + if (string->IsInternString()) { + return string; + } + + EcmaString *result = GetString(string); + if (result != nullptr) { + return result; + } + InternString(string); + return string; +} + +void EcmaStringTable::SweepWeakReference(const WeakRootVisitor &visitor) +{ + for (auto it = table_.begin(); it != table_.end();) { + auto *object = it->second; + auto fwd = visitor(object); + if (fwd == nullptr) { + LOG(DEBUG, GC) << "StringTable: delete string " << std::hex << object + << ", val = " << ConvertToString(object); + table_.erase(it++); + } else if (fwd != object) { + it->second = static_cast(fwd); + ++it; + LOG(DEBUG, GC) << "StringTable: forward " << std::hex << object << " -> " << fwd; + } else { + ++it; + } + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/ecma_string_table.h b/ecmascript/ecma_string_table.h new file mode 100644 index 0000000000000000000000000000000000000000..1e2061bf825b0b35953f86c936011fa3668491ef --- /dev/null +++ b/ecmascript/ecma_string_table.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_STRING_TABLE_H +#define PANDA_RUNTIME_ECMASCRIPT_STRING_TABLE_H + +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/mem/heap_roots.h" + +namespace panda::ecmascript { +class EcmaString; +class EcmaVM; + +class EcmaStringTable { +public: + explicit EcmaStringTable(const EcmaVM *vm); + virtual ~EcmaStringTable() + { + table_.clear(); + } + + void InternEmptyString(EcmaString *emptyStr); + EcmaString *GetOrInternString(const uint8_t *utf8Data, uint32_t utf8Len); + EcmaString *GetOrInternString(const uint16_t *utf16Data, uint32_t utf16Len); + EcmaString *GetOrInternString(EcmaString *string); + + void SweepWeakReference(const WeakRootVisitor &visitor); + +private: + NO_COPY_SEMANTIC(EcmaStringTable); + NO_MOVE_SEMANTIC(EcmaStringTable); + + EcmaString *GetString(const uint8_t *utf8Data, uint32_t utf8Len) const; + EcmaString *GetString(const uint16_t *utf16Data, uint32_t utf16Len) const; + EcmaString *GetString(EcmaString *string) const; + + void InternString(EcmaString *string); + + void InsertStringIfNotExist(EcmaString *string) + { + EcmaString *str = GetString(string); + if (str == nullptr) { + InternString(string); + } + } + + CUnorderedMultiMap table_; + const EcmaVM *vm_{nullptr}; + friend class SnapShotSerialize; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_STRING_TABLE_H diff --git a/ecmascript/ecma_vm.cpp b/ecmascript/ecma_vm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4962c89386b6eec4224c2055dc202716323f4cbc --- /dev/null +++ b/ecmascript/ecma_vm.cpp @@ -0,0 +1,739 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" + +#include "ecmascript/base/string_helper.h" +#include "ecmascript/builtins.h" +#include "ecmascript/builtins/builtins_regexp.h" +#include "ecmascript/class_linker/panda_file_translator.h" +#include "ecmascript/class_linker/program_object-inl.h" +#include "ecmascript/ecma_module.h" +#include "ecmascript/ecma_string_table.h" +#include "ecmascript/global_env.h" +#include "ecmascript/global_env_constants-inl.h" +#include "ecmascript/global_env_constants.h" +#include "ecmascript/global_handle_collection.h" +#include "ecmascript/ic/properties_cache-inl.h" +#include "ecmascript/jobs/micro_job_queue.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_for_in_iterator.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/heap.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/regexp/regexp_parser_cache.h" +#include "ecmascript/runtime_call_id.h" +#include "ecmascript/snapshot/mem/slot_bit.h" +#include "ecmascript/snapshot/mem/snapshot.h" +#include "ecmascript/snapshot/mem/snapshot_serialize.h" +#include "ecmascript/symbol_table.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tagged_queue-inl.h" +#include "ecmascript/tagged_queue.h" +#include "ecmascript/template_map.h" +#include "ecmascript/vmstat/runtime_stat.h" +#include "include/runtime_notification.h" +#include "libpandafile/file.h" +#include "trace/trace.h" + +namespace panda::ecmascript { +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static const std::string_view ENTRY_POINTER = "_GLOBAL::func_main_0"; + +/* static */ +EcmaVM *EcmaVM::Create(const RuntimeOptions &options) +{ + auto runtime = Runtime::GetCurrent(); + auto vm = runtime->GetInternalAllocator()->New(options); + if (UNLIKELY(vm == nullptr)) { + LOG_ECMA(ERROR) << "Failed to create jsvm"; + return nullptr; + } + auto jsThread = JSThread::Create(runtime, vm); + vm->thread_ = jsThread; + vm->Initialize(); + return vm; +} + +// static +bool EcmaVM::Destroy(PandaVM *vm) +{ + if (vm != nullptr) { + auto runtime = Runtime::GetCurrent(); + runtime->GetInternalAllocator()->Delete(vm); + return true; + } + return false; +} + +// static +Expected EcmaVM::Create(Runtime *runtime) +{ + EcmaVM *vm = runtime->GetInternalAllocator()->New(); + auto jsThread = ecmascript::JSThread::Create(runtime, vm); + vm->thread_ = jsThread; + return vm; +} + +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +EcmaVM::EcmaVM() : EcmaVM(Runtime::GetOptions()) +{ + isTestMode_ = true; +} + +EcmaVM::EcmaVM(RuntimeOptions options) + : options_(std::move(options)), + stringTable_(new EcmaStringTable(this)), + regionFactory_(std::make_unique()), + chunk_(regionFactory_.get()), + allocator_(new CAddressAllocator()), + nativeMethods_(&chunk_) +{ + icEnable_ = options_.IsIcEnable(); + gcStats_ = chunk_.New(); + rendezvous_ = chunk_.New(); + snapshotSerializeEnable_ = options_.IsSnapshotSerializeEnabled(); + if (!snapshotSerializeEnable_) { + snapshotDeserializeEnable_ = options_.IsSnapshotDeserializeEnabled(); + } + fileName_ = options_.GetSnapshotFile(); + frameworkAbcFileName_ = options_.GetFrameworkAbcFile(); +} + +bool EcmaVM::Initialize() +{ + ASSERT(thread_ != nullptr); + trace::ScopedTrace scoped_trace("EcmaVM::Initialize"); + + auto globalConst = const_cast(thread_->GlobalConstants()); + + propertiesCache_ = new PropertiesCache(); + regExpParserCache_ = new RegExpParserCache(); + heap_ = new Heap(this); + heap_->SetUp(); + factory_ = chunk_.New(thread_, heap_); + if (UNLIKELY(factory_ == nullptr)) { + LOG_ECMA(FATAL) << "alloc factory_ failed"; + UNREACHABLE(); + } + + if (!snapshotDeserializeEnable_ || !VerifyFilePath(fileName_)) { + LOG_ECMA(DEBUG) << "EcmaVM::Initialize run builtins"; + [[maybe_unused]] EcmaHandleScope scope(thread_); + + JSHandle dynClassClassHandle = factory_->NewEcmaDynClass(nullptr, JSHClass::SIZE, JSType::HCLASS); + JSHClass *dynclass = reinterpret_cast(dynClassClassHandle.GetTaggedValue().GetTaggedObject()); + dynclass->SetClass(dynclass); + JSHandle globalEnvClass = + factory_->NewEcmaDynClass(*dynClassClassHandle, GlobalEnv::SIZE, JSType::GLOBAL_ENV); + + JSHandle globalEnvHandle = factory_->NewGlobalEnv(*globalEnvClass); + globalEnv_ = globalEnvHandle.GetTaggedValue(); + auto globalEnv = GlobalEnv::Cast(globalEnv_.GetTaggedObject()); + + // init global env + globalConst->InitRootsClass(thread_, *dynClassClassHandle); + factory_->ObtainRootClass(GetGlobalEnv()); + globalConst->InitGlobalConstant(thread_); + globalEnv->SetEmptyArray(thread_, factory_->NewEmptyArray()); + globalEnv->SetEmptyLayoutInfo(thread_, factory_->CreateLayoutInfo(0)); + globalEnv->SetRegisterSymbols(thread_, JSTaggedValue(SymbolTable::Create(thread_))); + JSTaggedValue emptyStr = thread_->GlobalConstants()->GetEmptyString(); + stringTable_->InternEmptyString(EcmaString::Cast(emptyStr.GetTaggedObject())); + globalEnv->SetEmptyTaggedQueue(thread_, factory_->NewTaggedQueue(0)); + globalEnv->SetTemplateMap(thread_, JSTaggedValue(TemplateMap::Create(thread_))); + globalEnv->SetRegisterSymbols(GetJSThread(), JSTaggedValue(SymbolTable::Create(GetJSThread()))); + + SetupRegExpResultCache(); + microJobQueue_ = factory_->NewMicroJobQueue().GetTaggedValue(); + + { + Builtins builtins; + builtins.Initialize(globalEnvHandle, thread_); + } + } else { + LOG_ECMA(DEBUG) << "EcmaVM::Initialize run snapshot"; + SnapShot snapShot(this); + std::unique_ptr pf = snapShot.DeserializeGlobalEnvAndProgram(fileName_); + frameworkPandaFile_ = pf.get(); + AddPandaFile(pf.release(), false); + SetProgram(Program::Cast(frameworkProgram_.GetTaggedObject()), frameworkPandaFile_); + globalConst->InitGlobalUndefined(); + + factory_->ObtainRootClass(GetGlobalEnv()); + } + + moduleManager_ = new ModuleManager(this); + InitializeFinish(); + Runtime::GetCurrent()->GetNotificationManager()->VmStartEvent(); + Runtime::GetCurrent()->GetNotificationManager()->VmInitializationEvent(0); + return true; +} + +void EcmaVM::InitializeEcmaScriptRunStat() +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + static const char *runtimeCallerNames[] = { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTERPRETER_CALLER_NAME(name) "InterPreter::" #name, + INTERPRETER_CALLER_LIST(INTERPRETER_CALLER_NAME) // NOLINTNEXTLINE(bugprone-suspicious-missing-comma) +#undef INTERPRETER_CALLER_NAME +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define BUILTINS_API_NAME(class, name) "BuiltinsApi::" #class "_" #name, + BUITINS_API_LIST(BUILTINS_API_NAME) +#undef BUILTINS_API_NAME +#define ABSTRACT_OPERATION_NAME(class, name) "AbstractOperation::" #class "_" #name, + ABSTRACT_OPERATION_LIST(ABSTRACT_OPERATION_NAME) +#undef ABSTRACT_OPERATION_NAME + }; + runtimeStat_ = chunk_.New(runtimeCallerNames, ecmascript::RUNTIME_CALLER_NUMBER); + if (UNLIKELY(runtimeStat_ == nullptr)) { + LOG_ECMA(FATAL) << "alloc runtimeStat_ failed"; + UNREACHABLE(); + } +} + +void EcmaVM::SetRuntimeStatEnable(bool flag) +{ + if (flag) { + if (runtimeStat_ == nullptr) { + InitializeEcmaScriptRunStat(); + } + } else { + if (runtimeStatEnabled_) { + runtimeStat_->Print(); + runtimeStat_->ResetAllCount(); + } + } + runtimeStatEnabled_ = flag; +} + +bool EcmaVM::InitializeFinish() +{ + vmInitialized_ = true; + return true; +} +EcmaVM::~EcmaVM() +{ + vmInitialized_ = false; + ClearNativeMethodsData(); + + if (runtimeStat_ != nullptr && runtimeStatEnabled_) { + runtimeStat_->Print(); + } + + // clear c_address: c++ pointer delete + ClearBufferData(); + + if (heap_ != nullptr) { + heap_->TearDown(); + delete heap_; + heap_ = nullptr; + } + + delete propertiesCache_; + propertiesCache_ = nullptr; + + delete regExpParserCache_; + regExpParserCache_ = nullptr; + + if (factory_ != nullptr) { + chunk_.Delete(factory_); + factory_ = nullptr; + } + + if (gcStats_ != nullptr) { + gcStats_->PrintStatisticResult(); + chunk_.Delete(gcStats_); + gcStats_ = nullptr; + } + + if (stringTable_ != nullptr) { + delete stringTable_; + stringTable_ = nullptr; + } + + if (runtimeStat_ != nullptr) { + chunk_.Delete(runtimeStat_); + runtimeStat_ = nullptr; + } + + if (moduleManager_ != nullptr) { + delete moduleManager_; + moduleManager_ = nullptr; + } + + if (thread_ != nullptr) { + delete thread_; + thread_ = nullptr; + } + + if (allocator_ != nullptr) { + delete allocator_; + allocator_ = nullptr; + } + + extractorCache_.clear(); + frameworkProgramMethods_.clear(); +} + +bool EcmaVM::ExecuteFromPf(std::string_view filename, std::string_view entryPoint, const std::vector &args, + bool isModule) +{ + const panda_file::File *pf_ptr = nullptr; + if (frameworkPandaFile_ == nullptr || !IsFrameworkPandaFile(filename)) { + auto pf = panda_file::OpenPandaFileOrZip(filename, panda_file::File::READ_WRITE); + if (pf == nullptr) { + return false; + } + pf_ptr = pf.get(); + AddPandaFile(pf.release(), isModule); // Store here prevent from being automatically cleared + } else { + pf_ptr = frameworkPandaFile_; + } + + return Execute(*pf_ptr, entryPoint, args); +} + +bool EcmaVM::ExecuteFromBuffer(const void *buffer, size_t size, std::string_view entryPoint, + const std::vector &args) +{ + auto pf = panda_file::OpenPandaFileFromMemory(buffer, size); + if (pf == nullptr) { + return false; + } + const panda_file::File *pf_ptr = pf.get(); + AddPandaFile(pf.release(), false); // Store here from being automatically cleared + + return Execute(*pf_ptr, entryPoint, args); +} + +tooling::ecmascript::PtJSExtractor *EcmaVM::GetDebugInfoExtractor(const panda_file::File *file) +{ + tooling::ecmascript::PtJSExtractor *res = nullptr; + auto it = extractorCache_.find(file); + if (it == extractorCache_.end()) { + auto extractor = std::make_unique(file); + res = extractor.get(); + extractorCache_[file] = std::move(extractor); + } else { + res = it->second.get(); + } + return res; +} + +bool EcmaVM::Execute(const panda_file::File &pf, std::string_view entryPoint, const std::vector &args) +{ + // Get ClassName and MethodName + size_t pos = entryPoint.find_last_of("::"); + if (pos == std::string_view::npos) { + LOG_ECMA(ERROR) << "EntryPoint:" << entryPoint << " is illegal"; + return false; + } + CString methodName(entryPoint.substr(pos + 1)); + + // For Ark application startup + InvokeEcmaEntrypoint(pf, methodName, args); + return true; +} + +JSHandle EcmaVM::GetGlobalEnv() const +{ + return JSHandle(reinterpret_cast(&globalEnv_)); +} + +JSHandle EcmaVM::GetMicroJobQueue() const +{ + return JSHandle(reinterpret_cast(µJobQueue_)); +} + +JSMethod *EcmaVM::GetMethodForNativeFunction(const void *func) +{ + // signature: any foo(any function_obj, any this) + uint32_t accessFlags = ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_NATIVE; + uint32_t numArgs = 2; // function object and this + + auto method = chunk_.New(nullptr, nullptr, panda_file::File::EntityId(0), panda_file::File::EntityId(0), + accessFlags, numArgs, nullptr); + method->SetNativePointer(const_cast(func)); + + nativeMethods_.push_back(method); + return nativeMethods_.back(); +} + +void EcmaVM::RedirectMethod(const panda_file::File &pf) +{ + for (auto method : frameworkProgramMethods_) { + method->SetPandaFile(&pf); + } +} + +Expected EcmaVM::InvokeEntrypointImpl(Method *entrypoint, const std::vector &args) +{ + // For testcase startup + const panda_file::File *file = entrypoint->GetPandaFile(); + AddPandaFile(file, false); + return InvokeEcmaEntrypoint(*file, utf::Mutf8AsCString(entrypoint->GetName().data), args); +} + +Expected EcmaVM::InvokeEcmaEntrypoint(const panda_file::File &pf, const CString &methodName, + const std::vector &args) +{ + thread_->SetIsEcmaInterpreter(true); + thread_->SetIsSnapshotMode(true); + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle program; + if (snapshotSerializeEnable_) { + program = PandaFileTranslator::TranslatePandaFile(this, pf, methodName); + auto string = EcmaString::Cast(program->GetLocation().GetTaggedObject()); + + auto index = ConvertToString(string).find(frameworkAbcFileName_); + if (index != CString::npos) { + LOG_ECMA(DEBUG) << "snapShot MakeSnapShotProgramObject abc " << ConvertToString(string); + SnapShot snapShot(this); + snapShot.MakeSnapShotProgramObject(*program, &pf, fileName_); + } + } else { + if (&pf != frameworkPandaFile_) { + program = PandaFileTranslator::TranslatePandaFile(this, pf, methodName); + } else { + JSHandle string = factory_->NewFromStdString(pf.GetFilename()); + program = JSHandle(thread_, frameworkProgram_); + program->SetLocation(thread_, string); + RedirectMethod(pf); + } + } + + SetProgram(*program, &pf); + if (program.IsEmpty()) { + LOG_ECMA(ERROR) << "program is empty, invoke entrypoint failed"; + return Unexpected(Runtime::Error::PANDA_FILE_LOAD_ERROR); + } + + JSHandle func = JSHandle(thread_, program->GetMainFunction()); + JSHandle global = GlobalEnv::Cast(globalEnv_.GetTaggedObject())->GetJSGlobalObject(); + JSHandle newTarget(thread_, JSTaggedValue::Undefined()); + JSHandle jsargs = factory_->NewTaggedArray(args.size()); + array_size_t i = 0; + for (const std::string &str : args) { + JSHandle strobj(factory_->NewFromStdString(str)); + jsargs->Set(thread_, i++, strobj); + } + + panda::ecmascript::InvokeJsFunction(thread_, func, global, newTarget, jsargs); + if (!thread_->HasPendingException()) { + job::MicroJobQueue::Cast(microJobQueue_.GetTaggedObject())->ExecutePendingJob(thread_); + } + + // print exception information + if (thread_->HasPendingException()) { + auto exception = thread_->GetException(); + HandleUncaughtException(exception.GetTaggedObject()); + } + return 0; +} + +void EcmaVM::AddPandaFile(const panda_file::File *pf, bool isModule) +{ + ASSERT(pf != nullptr); + pandaFileWithProgram_.push_back(std::make_tuple(nullptr, pf, isModule)); + + // for debugger + Runtime::GetCurrent()->GetNotificationManager()->LoadModuleEvent(pf->GetFilename()); +} + +void EcmaVM::SetProgram(Program *program, const panda_file::File *pf) +{ + auto it = std::find_if(pandaFileWithProgram_.begin(), pandaFileWithProgram_.end(), + [pf](auto entry) { return std::get<1>(entry) == pf; }); + ASSERT(it != pandaFileWithProgram_.end()); + std::get<0>(*it) = program; +} + +bool EcmaVM::IsFrameworkPandaFile(std::string_view filename) const +{ + return filename.size() >= frameworkAbcFileName_.size() && + filename.substr(filename.size() - frameworkAbcFileName_.size()) == frameworkAbcFileName_; +} + +JSHandle EcmaVM::GetEcmaUncaughtException() const +{ + if (thread_->GetException().IsHole()) { + return JSHandle(); + } + JSHandle exceptionHandle(thread_, thread_->GetException()); + thread_->ClearException(); // clear for ohos app + if (exceptionHandle->IsObjectWrapper()) { + JSHandle wrapperValue = JSHandle::Cast(exceptionHandle); + JSHandle throwValue(thread_, wrapperValue->GetValue()); + return throwValue; + } + + return exceptionHandle; +} + +void EcmaVM::EnableUserUncaughtErrorHandler() +{ + isUncaughtExceptionRegistered_ = true; +} + +void EcmaVM::HandleUncaughtException(ObjectHeader *exception) +{ + if (isUncaughtExceptionRegistered_) { + return; + } + [[maybe_unused]] EcmaHandleScope handle_scope(thread_); + JSHandle exceptionHandle(thread_, JSTaggedValue(exception)); + // if caught exceptionHandle type is JSError + thread_->ClearException(); + if (exceptionHandle->IsJSError()) { + PrintJSErrorInfo(exceptionHandle); + return; + } + if (exceptionHandle->IsObjectWrapper()) { + JSHandle wrapperValue = JSHandle::Cast(exceptionHandle); + JSHandle throwValue(thread_, wrapperValue->GetValue()); + if (throwValue->IsJSError()) { + PrintJSErrorInfo(throwValue); + } else { + JSHandle result = JSTaggedValue::ToString(thread_, throwValue); + CString string = ConvertToString(*result); + LOG(ERROR, RUNTIME) << string; + } + } +} + +void EcmaVM::PrintJSErrorInfo(const JSHandle &exceptionInfo) +{ + JSHandle nameKey = thread_->GlobalConstants()->GetHandledNameString(); + JSHandle name(JSObject::GetProperty(thread_, exceptionInfo, nameKey).GetValue()); + JSHandle msgKey = thread_->GlobalConstants()->GetHandledMessageString(); + JSHandle msg(JSObject::GetProperty(thread_, exceptionInfo, msgKey).GetValue()); + JSHandle stackKey = thread_->GlobalConstants()->GetHandledStackString(); + JSHandle stack(JSObject::GetProperty(thread_, exceptionInfo, stackKey).GetValue()); + + CString nameBuffer = ConvertToString(*name); + CString msgBuffer = ConvertToString(*msg); + CString stackBuffer = ConvertToString(*stack); + LOG(ERROR, RUNTIME) << nameBuffer << ": " << msgBuffer << "\n" << stackBuffer; +} + +void EcmaVM::ProcessReferences(const WeakRootVisitor &v0) +{ + if (propertiesCache_ != nullptr) { + propertiesCache_->Clear(); + } + + if (regExpParserCache_ != nullptr) { + regExpParserCache_->Clear(); + } + + // array buffer + for (auto iter = arrayBufferDataList_.begin(); iter != arrayBufferDataList_.end();) { + JSNativePointer *object = *iter; + auto fwd = v0(reinterpret_cast(object)); + if (fwd == nullptr) { + object->Destroy(); + iter = arrayBufferDataList_.erase(iter); + } else if (fwd != reinterpret_cast(object)) { + *iter = JSNativePointer::Cast(fwd); + ++iter; + } else { + ++iter; + } + } + + // program vector + for (auto iter = pandaFileWithProgram_.begin(); iter != pandaFileWithProgram_.end();) { + auto object = std::get<0>(*iter); + if (object != nullptr) { + auto fwd = v0(object); + if (fwd == nullptr) { + object->FreeMethodData(regionFactory_.get()); + auto pf = std::get<1>(*iter); + extractorCache_.erase(pf); + delete pf; + iter = pandaFileWithProgram_.erase(iter); + } else if (fwd != object) { + *iter = std::make_tuple(reinterpret_cast(fwd), std::get<1>(*iter), + std::get<2>(*iter)); // 2: index + ++iter; + } else { + ++iter; + } + } else { + ++iter; + } + } + + // framework program + if (!frameworkProgram_.IsHole()) { + auto fwd = v0(frameworkProgram_.GetTaggedObject()); + if (fwd == nullptr) { + frameworkProgram_ = JSTaggedValue::Undefined(); + } else if (fwd != frameworkProgram_.GetTaggedObject()) { + frameworkProgram_ = JSTaggedValue(fwd); + } + } +} + +void EcmaVM::PushToArrayDataList(JSNativePointer *array) +{ + if (std::find(arrayBufferDataList_.begin(), arrayBufferDataList_.end(), array) != arrayBufferDataList_.end()) { + return; + } + arrayBufferDataList_.emplace_back(array); +} + +void EcmaVM::RemoveArrayDataList(JSNativePointer *array) +{ + auto iter = std::find(arrayBufferDataList_.begin(), arrayBufferDataList_.end(), array); + if (iter != arrayBufferDataList_.end()) { + arrayBufferDataList_.erase(iter); + } +} + +bool EcmaVM::VerifyFilePath(const CString &filePath) const +{ + if (filePath.size() > PATH_MAX) { + return false; + } + + CVector resolvedPath(PATH_MAX); + auto result = realpath(filePath.c_str(), resolvedPath.data()); + if (result == nullptr) { + return false; + } + std::ifstream file(resolvedPath.data()); + if (!file.good()) { + return false; + } + file.close(); + return true; +} + +void EcmaVM::ClearBufferData() +{ + for (auto iter : arrayBufferDataList_) { + iter->Destroy(); + } + arrayBufferDataList_.clear(); + + for (auto iter = pandaFileWithProgram_.begin(); iter != pandaFileWithProgram_.end();) { + std::get<0>(*iter)->FreeMethodData(regionFactory_.get()); + auto pf = std::get<1>(*iter); + // 2 : 2 means the third element. + if (pf == frameworkPandaFile_ || !isTestMode_ || std::get<2>(*iter)) { + // In testmode, panda file will free in classlinker + extractorCache_.erase(pf); + delete pf; + } + iter = pandaFileWithProgram_.erase(iter); + } + pandaFileWithProgram_.clear(); +} + +bool EcmaVM::ExecutePromisePendingJob() const +{ + if (!thread_->HasPendingException()) { + job::MicroJobQueue::Cast(microJobQueue_.GetTaggedObject())->ExecutePendingJob(thread_); + return true; + } + return false; +} + +void EcmaVM::CollectGarbage(TriggerGCType gcType) const +{ + heap_->CollectGarbage(gcType); +} + +void EcmaVM::StartHeapTracking(HeapTracker *tracker) +{ + heap_->StartHeapTracking(tracker); +} + +void EcmaVM::StopHeapTracking() +{ + heap_->StopHeapTracking(); +} + +void EcmaVM::Iterate(const RootVisitor &v) +{ + v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&globalEnv_))); + v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(µJobQueue_))); + v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&moduleManager_->ecmaModules_))); + v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(®expCache_))); +} + +void EcmaVM::SetGlobalEnv(GlobalEnv *global) +{ + ASSERT(global != nullptr); + globalEnv_ = JSTaggedValue(global); +} + +void EcmaVM::SetMicroJobQueue(job::MicroJobQueue *queue) +{ + ASSERT(queue != nullptr); + microJobQueue_ = JSTaggedValue(queue); +} + +JSHandle EcmaVM::GetModuleByName(JSHandle moduleName) +{ + CString dirPath; + CString scriptName = ConvertToString(EcmaString::Cast(moduleName->GetTaggedObject())); + + // need to check abc file + auto pos = scriptName.find_last_of('.'); + CString abcPath = dirPath.append(scriptName.substr(0, pos == std::string::npos ? 0 : pos)).append(".abc"); + + // Uniform module name + JSHandle abcModuleName = factory_->NewFromString(abcPath); + + JSHandle module = moduleManager_->GetModule(thread_, JSHandle::Cast(abcModuleName)); + if (module->IsUndefined()) { + CString file = ConvertToString(abcModuleName.GetObject()); + std::vector argv; + ExecuteModule(file, ENTRY_POINTER, argv); + module = moduleManager_->GetModule(thread_, JSHandle::Cast(abcModuleName)); + } + return module; +} + +void EcmaVM::ExecuteModule(std::string_view moduleFile, std::string_view entryPoint, + const std::vector &args) +{ + moduleManager_->SetCurrentExportModuleName(moduleFile); + // Update Current Module + thread_->SetIsEcmaInterpreter(true); + EcmaVM::ExecuteFromPf(moduleFile, entryPoint, args, true); + // Restore Current Module + moduleManager_->RestoreCurrentExportModuleName(); +} + +void EcmaVM::ClearNativeMethodsData() +{ + for (auto iter : nativeMethods_) { + chunk_.Delete(iter); + } + nativeMethods_.clear(); +} + +void EcmaVM::SetupRegExpResultCache() +{ + regexpCache_ = builtins::RegExpExecResultCache::CreateCacheTable(thread_); +} + +} // namespace panda::ecmascript diff --git a/ecmascript/ecma_vm.h b/ecmascript/ecma_vm.h new file mode 100644 index 0000000000000000000000000000000000000000..d77a3bc56581c4d734c05fb7d837107c761ae96d --- /dev/null +++ b/ecmascript/ecma_vm.h @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMA_VM_H +#define PANDA_RUNTIME_ECMA_VM_H + +#include + +#include "ecmascript/ecma_string_table.h" +#include "ecmascript/global_handle_collection.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_method.h" +#include "ecmascript/js_native_pointer.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/mem/c_string.h" +#include "ecmascript/mem/chunk_containers.h" +#include "ecmascript/mem/gc_stats.h" +#include "ecmascript/mem/heap_roots.h" +#include "ecmascript/mem/space.h" +#include "ecmascript/snapshot/mem/snapshot_serialize.h" +#include "ecmascript/tooling/pt_js_extractor.h" +#include "include/panda_vm.h" +#include "libpandabase/macros.h" +#include "libpandabase/os/library_loader.h" + +namespace panda { +namespace panda_file { +class File; +} // namespace panda_file + +namespace ecmascript { +class GlobalEnv; +class ObjectFactory; +class PropertiesCache; +class RegExpParserCache; +class EcmaRuntimeStat; +class EcmaHeapManager; +class Heap; +class HeapTracker; +class Program; + +namespace job { +class MicroJobQueue; +} // namespace job + +template +class JSHandle; +class JSArrayBuffer; +class JSFunction; +class Program; +class ModuleManager; +class EcmaModule; + +class EcmaVM : public PandaVM { + using PtJSExtractor = tooling::ecmascript::PtJSExtractor; + +public: + static EcmaVM *Cast(PandaVM *object) + { + return reinterpret_cast(object); + } + + static EcmaVM *Create(const RuntimeOptions &options); + + static bool Destroy(PandaVM *vm); + + explicit EcmaVM(RuntimeOptions options); + + static Expected Create([[maybe_unused]] Runtime *runtime); + + EcmaVM(); + + ~EcmaVM() override; + + bool ExecuteFromPf(std::string_view filename, std::string_view entryPoint, const std::vector &args, + bool isModule = false); + + bool ExecuteFromBuffer(const void *buffer, size_t size, std::string_view entryPoint, + const std::vector &args); + + PtJSExtractor *GetDebugInfoExtractor(const panda_file::File *file); + + bool IsInitialized() const + { + return vmInitialized_; + } + + ObjectFactory *GetFactory() const + { + return factory_; + } + + bool Initialize() override; + + bool InitializeFinish() override; + void UninitializeThreads() override {} + void PreStartup() override {} + void PreZygoteFork() override {} + void PostZygoteFork() override {} + void InitializeGC() override {} + void StartGC() override {} + void StopGC() override {} + + void VisitVmRoots([[maybe_unused]] const GCRootVisitor &visitor) override {} + void UpdateVmRefs() override {} + + PandaVMType GetPandaVMType() const override + { + return PandaVMType::ECMA_VM; + } + + LanguageContext GetLanguageContext() const override + { + return Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ECMASCRIPT); + } + + panda::mem::HeapManager *GetHeapManager() const override + { + return nullptr; + } + + panda::mem::GC *GetGC() const override + { + return nullptr; + } + + panda::mem::GCTrigger *GetGCTrigger() const override + { + return nullptr; + } + + StringTable *GetStringTable() const override + { + return nullptr; + } + + panda::mem::GCStats *GetGCStats() const override + { + return nullptr; + } + + panda::mem::MemStatsType *GetMemStats() const override + { + return nullptr; + } + + GCStats *GetEcmaGCStats() const + { + return gcStats_; + } + + panda::mem::GlobalObjectStorage *GetGlobalObjectStorage() const override + { + return nullptr; + } + + MonitorPool *GetMonitorPool() const override + { + return nullptr; + } + + ThreadManager *GetThreadManager() const override + { + return nullptr; + } + + ManagedThread *GetAssociatedThread() const override + { + return thread_; + } + + JSThread *GetAssociatedJSThread() const + { + return thread_; + } + + CompilerInterface *GetCompiler() const override + { + return nullptr; + } + + Rendezvous *GetRendezvous() const override + { + return rendezvous_; + } + + ObjectHeader *GetOOMErrorObject() override + { + // preallocated OOM is not implemented for JS + UNREACHABLE(); + } + + panda::mem::ReferenceProcessor *GetReferenceProcessor() const override + { + return nullptr; + } + + const RuntimeOptions &GetOptions() const override + { + return options_; + } + + JSHandle GetGlobalEnv() const; + + JSHandle GetMicroJobQueue() const; + + bool ExecutePromisePendingJob() const; + + PropertiesCache *GetPropertiesCache() const + { + return propertiesCache_; + } + + RegExpParserCache *GetRegExpParserCache() const + { + ASSERT(regExpParserCache_ != nullptr); + return regExpParserCache_; + } + + JSMethod *GetMethodForNativeFunction(const void *func); + + EcmaStringTable *GetEcmaStringTable() const + { + ASSERT(stringTable_ != nullptr); + return stringTable_; + } + + JSThread *GetJSThread() const + { + return thread_; + } + + bool ICEnable() const + { + return icEnable_; + } + + void PushToArrayDataList(JSNativePointer *array); + void RemoveArrayDataList(JSNativePointer *array); + + JSHandle GetEcmaUncaughtException() const; + void EnableUserUncaughtErrorHandler(); + + template + void EnumeratePandaFiles(Callback cb) const + { + for (const auto &iter : pandaFileWithProgram_) { + if (!cb(std::get<0>(iter), std::get<1>(iter))) { + break; + } + } + } + + template + void EnumerateProgram(Callback cb, std::string pandaFile) const + { + for (const auto &iter : pandaFileWithProgram_) { + if (pandaFile == std::get<1>(iter)->GetFilename()) { + cb(std::get<0>(iter)); + break; + } + } + } + + EcmaRuntimeStat *GetRuntimeStat() const + { + return runtimeStat_; + } + + void SetRuntimeStatEnable(bool flag); + + bool IsRuntimeStatEnabled() const + { + return runtimeStatEnabled_; + } + + void Iterate(const RootVisitor &v); + + const Heap *GetHeap() const + { + return heap_; + } + + void CollectGarbage(TriggerGCType gcType) const; + + void StartHeapTracking(HeapTracker *tracker); + + void StopHeapTracking(); + + RegionFactory *GetRegionFactory() const + { + return regionFactory_.get(); + } + + Chunk *GetChunk() const + { + return const_cast(&chunk_); + } + + void ProcessReferences(const WeakRootVisitor &v0); + + JSHandle GetModuleByName(JSHandle moduleName); + + void ExecuteModule(std::string_view moduleFile, std::string_view entryPoint, const std::vector &args); + + ModuleManager *GetModuleManager() const + { + return moduleManager_; + } + + CAddressAllocator *GetCountAllocator() const + { + return allocator_; + } + + static constexpr uint32_t GetGlobalEnvOffset() + { + return MEMBER_OFFSET(EcmaVM, globalEnv_); + } + + static constexpr uint32_t GetMicroJobQueueOffset() + { + return MEMBER_OFFSET(EcmaVM, microJobQueue_); + } + + void SetupRegExpResultCache(); + JSHandle GetRegExpCache() + { + return JSHandle(reinterpret_cast(®expCache_)); + } + + friend class ValueSerializer; + +protected: + bool CheckEntrypointSignature([[maybe_unused]] Method *entrypoint) override + { + return true; + } + + Expected InvokeEntrypointImpl(Method *entrypoint, + const std::vector &args) override; + + void HandleUncaughtException(ObjectHeader *exception) override; + + void PrintJSErrorInfo(const JSHandle &exceptionInfo); + +private: + void AddPandaFile(const panda_file::File *pf, bool isModule); + void SetProgram(Program *program, const panda_file::File *pf); + bool IsFrameworkPandaFile(std::string_view filename) const; + + void SetGlobalEnv(GlobalEnv *global); + + void SetMicroJobQueue(job::MicroJobQueue *queue); + + bool Execute(const panda_file::File &pf, std::string_view entryPoint, const std::vector &args); + + Expected InvokeEcmaEntrypoint(const panda_file::File &pf, const CString &methodName, + const std::vector &args); + + void InitializeEcmaScriptRunStat(); + + void RedirectMethod(const panda_file::File &pf); + + bool VerifyFilePath(const CString &filePath) const; + + void ClearBufferData(); + + void ClearNativeMethodsData(); + + NO_MOVE_SEMANTIC(EcmaVM); + NO_COPY_SEMANTIC(EcmaVM); + + // init EcmaVM construct + RuntimeOptions options_; + EcmaStringTable *stringTable_; + std::unique_ptr regionFactory_; + Chunk chunk_; + CAddressAllocator *allocator_{nullptr}; + ChunkVector nativeMethods_; + bool icEnable_{true}; + GCStats *gcStats_ = nullptr; + Rendezvous *rendezvous_{nullptr}; + bool snapshotSerializeEnable_{false}; + bool snapshotDeserializeEnable_{false}; + CString fileName_; + CString frameworkAbcFileName_; + + bool isTestMode_{false}; + + // init EcmaVM Create + JSThread *thread_{nullptr}; + + // init EcmaVM Initialize + PropertiesCache *propertiesCache_{nullptr}; + RegExpParserCache *regExpParserCache_{nullptr}; + Heap *heap_{nullptr}; + ObjectFactory *factory_{nullptr}; + JSTaggedValue globalEnv_{JSTaggedValue::Hole()}; + JSTaggedValue regexpCache_{JSTaggedValue::Hole()}; + JSTaggedValue microJobQueue_{JSTaggedValue::Hole()}; + ModuleManager *moduleManager_{nullptr}; + bool vmInitialized_{false}; + + // Runtime initialization + CUnorderedMap> extractorCache_; + const panda_file::File *frameworkPandaFile_ {nullptr}; + CVector frameworkProgramMethods_; + EcmaRuntimeStat *runtimeStat_ {nullptr}; // maybe nullptr + bool runtimeStatEnabled_ {false}; + bool isUncaughtExceptionRegistered_ {false}; + + // weak reference need Redirect address + JSTaggedValue frameworkProgram_ {JSTaggedValue::Hole()}; // no mark for gc + CVector arrayBufferDataList_; + CVector> pandaFileWithProgram_; + + friend class SnapShotSerialize; + friend class ObjectFactory; +}; +} // namespace ecmascript +} // namespace panda + +#endif diff --git a/ecmascript/free_object.cpp b/ecmascript/free_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..46acd047b6012bfa4724e75387ee3c34df60fcb9 --- /dev/null +++ b/ecmascript/free_object.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/free_object.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +FreeObject *FreeObject::FillFreeObject(EcmaVM *vm, uintptr_t address, size_t size) +{ + return vm->GetFactory()->FillFreeObject(address, size); +} +} // namespace panda::ecmascript diff --git a/ecmascript/free_object.h b/ecmascript/free_object.h new file mode 100644 index 0000000000000000000000000000000000000000..1ff055838ddd5de52244d38685865afba89b9b34 --- /dev/null +++ b/ecmascript/free_object.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_FREE_OBJECT_H +#define PANDA_RUNTIME_ECMASCRIPT_FREE_OBJECT_H + +#include "ecmascript/js_hclass.h" +#include "ecmascript/mem/barriers.h" +#include "ecmascript/mem/tagged_object-inl.h" + +namespace panda::ecmascript { +class FreeObject : public TaggedObject { +public: + static FreeObject *Cast(uintptr_t object) + { + return reinterpret_cast(object); + } + static FreeObject *FillFreeObject(EcmaVM *vm, uintptr_t address, size_t size); + + inline bool IsEmpty() const + { + return Available() == 0; + } + + inline uintptr_t GetBegin() const + { + return reinterpret_cast(this); + } + + inline uintptr_t GetEnd() const + { + return reinterpret_cast(this) + Available(); + } + + inline void SetAvailable(size_t size) + { + if (size >= SIZE) { + SetSize(size); + } + } + + inline size_t Available() const + { + auto hclass = GetClass(); + if (hclass != nullptr && (hclass->IsFreeObjectWithOneField() || hclass->IsFreeObjectWithNoneField())) { + return hclass->GetObjectSize(); + } + return GetSize(); + } + + inline bool IsFreeObject() const + { + auto hclass = GetClass(); + return (hclass == nullptr) || (hclass->IsFreeObjectWithOneField() || hclass->IsFreeObjectWithTwoField() || + hclass->IsFreeObjectWithNoneField()); + } + + static constexpr size_t NEXT_OFFSET = TaggedObject::ObjectHeaderSize(); + SET_GET_NATIVE_FIELD(Next, FreeObject, NEXT_OFFSET, SIZE_OFFSET); + SET_GET_PRIMITIVE_FIELD(Size, size_t, SIZE_OFFSET, SIZE); +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_FREE_OBJECT_H diff --git a/ecmascript/generator_helper.cpp b/ecmascript/generator_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba782403a4f50eae425ef5b6983c0b89b83feb17 --- /dev/null +++ b/ecmascript/generator_helper.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/generator_helper.h" + +#include "ecmascript/interpreter/interpreter-inl.h" +#include "ecmascript/js_iterator.h" +#include "libpandafile/bytecode_instruction-inl.h" + +namespace panda::ecmascript { +JSHandle GeneratorHelper::Next(JSThread *thread, const JSHandle &genContext, + JSTaggedValue value) +{ + JSHandle genObject(thread, genContext->GetGeneratorObject()); + genObject->SetGeneratorState(thread, JSTaggedValue(static_cast(JSGeneratorState::EXECUTING))); + genObject->SetResumeMode(thread, JSTaggedValue(static_cast(GeneratorResumeMode::NEXT))); + genObject->SetResumeResult(thread, value); + + JSTaggedValue next = EcmaInterpreter::GeneratorReEnterInterpreter(thread, genContext); + JSHandle nextValue(thread, next); + + if (genObject->IsSuspendYield()) { + return JSHandle::Cast(nextValue); + } + genObject->SetGeneratorState(thread, JSTaggedValue(static_cast(JSGeneratorState::COMPLETED))); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + return JSIterator::CreateIterResultObject(thread, nextValue, true); +} + +JSHandle GeneratorHelper::Return(JSThread *thread, const JSHandle &genContext, + JSTaggedValue value) +{ + JSHandle genObject(thread, genContext->GetGeneratorObject()); + genObject->SetResumeMode(thread, JSTaggedValue(static_cast(GeneratorResumeMode::RETURN))); + genObject->SetResumeResult(thread, value); + + JSTaggedValue res = EcmaInterpreter::GeneratorReEnterInterpreter(thread, genContext); + JSHandle returnValue(thread, res); + // change state to completed + if (genObject->IsExecuting()) { + genObject->SetGeneratorState(thread, JSTaggedValue(static_cast(JSGeneratorState::COMPLETED))); + } + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + return JSIterator::CreateIterResultObject(thread, returnValue, true); +} + +JSHandle GeneratorHelper::Throw(JSThread *thread, const JSHandle &genContext, + JSTaggedValue value) +{ + JSHandle genObject(thread, genContext->GetGeneratorObject()); + genObject->SetResumeMode(thread, JSTaggedValue(static_cast(GeneratorResumeMode::THROW))); + genObject->SetResumeResult(thread, value); + + JSTaggedValue res = EcmaInterpreter::GeneratorReEnterInterpreter(thread, genContext); + JSHandle throwValue(thread, res); + + if (genObject->IsSuspendYield()) { + return JSHandle::Cast(throwValue); + } + + // change state to completed + genObject->SetGeneratorState(thread, JSTaggedValue(static_cast(JSGeneratorState::COMPLETED))); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + return JSIterator::CreateIterResultObject(thread, throwValue, true); +} + +// main->foo +void GeneratorHelper::ChangeGenContext(JSThread *thread, const JSHandle &genContext, + [[maybe_unused]] C2IBridge *c2i) +{ + JSThread *jsThread = thread; + ASSERT(jsThread->IsEcmaInterpreter()); + EcmaInterpreter::ChangeGenContext(jsThread, genContext); +} + +// foo->main +void GeneratorHelper::ResumeContext(JSThread *thread) +{ + ASSERT(thread->IsEcmaInterpreter()); + EcmaInterpreter::ResumeContext(thread); +} +} // namespace panda::ecmascript diff --git a/ecmascript/generator_helper.h b/ecmascript/generator_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..c5dedb209e2bd758b6107f6b09ae94f62bef6614 --- /dev/null +++ b/ecmascript/generator_helper.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_GENERATOR_HELPER_H +#define PANDA_RUNTIME_ECMASCRIPT_GENERATOR_HELPER_H + +#include + +#include "ecmascript/js_generator_object.h" +#include "ecmascript/js_handle.h" + +namespace panda::ecmascript { +enum class GeneratorResumeMode { + RETURN = 0, + THROW, + NEXT, +}; + +using C2IBridge = std::array; // 4: means array length + +class GeneratorHelper { +public: + static JSHandle Next(JSThread *thread, const JSHandle &genContext, JSTaggedValue value); + + static JSHandle Return(JSThread *thread, const JSHandle &genContext, + JSTaggedValue value); + + static JSHandle Throw(JSThread *thread, const JSHandle &genContext, + JSTaggedValue value); + static void ChangeGenContext(JSThread *thread, const JSHandle &genContext, C2IBridge *c2i); + static void ResumeContext(JSThread *thread); +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_GENERATOR_HELPER_H diff --git a/ecmascript/global_dictionary-inl.h b/ecmascript/global_dictionary-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..52a2ecbf2e94574460569e6ced7b86172b87a5ac --- /dev/null +++ b/ecmascript/global_dictionary-inl.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_GLOBAL_DICTIONARY_INL_H +#define PANDA_RUNTIME_GLOBAL_DICTIONARY_INL_H + +#include "ecmascript/global_dictionary.h" +#include "ecmascript/ic/property_box.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/tagged_hash_table-inl.h" + +namespace panda { +namespace ecmascript { +int GlobalDictionary::Hash(const JSTaggedValue &key) +{ + if (key.IsHeapObject()) { + if (key.IsSymbol()) { + auto symbolString = JSSymbol::Cast(key.GetTaggedObject()); + return static_cast(symbolString->GetHashField()).GetInt(); + } + if (key.IsString()) { + auto keyString = EcmaString::Cast(key.GetTaggedObject()); + return keyString->GetHashcode(); + } + } + // key must be object + UNREACHABLE(); +} + +bool GlobalDictionary::IsMatch(const JSTaggedValue &key, const JSTaggedValue &other) +{ + return key == other; +} + +PropertyBox *GlobalDictionary::GetBox(int entry) const +{ + int index = GetEntryIndex(entry) + ENTRY_VALUE_INDEX; + return PropertyBox::Cast(Get(index).GetTaggedObject()); +} + +JSTaggedValue GlobalDictionary::GetValue(int entry) const +{ + return GetBox(entry)->GetValue(); +} + +PropertyAttributes GlobalDictionary::GetAttributes(int entry) const +{ + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + return PropertyAttributes(Get(index).GetInt()); +} + +void GlobalDictionary::SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, + const PropertyAttributes &attributes) +{ + SetKey(thread, entry, key); + SetAttributes(thread, entry, attributes); + UpdateValueAndAttributes(thread, entry, value, attributes); +} + +void GlobalDictionary::ClearEntry(const JSThread *thread, int entry) +{ + JSTaggedValue hole = JSTaggedValue::Hole(); + PropertyAttributes metaData; + SetEntry(thread, entry, hole, hole, metaData); +} + +void GlobalDictionary::UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, + const PropertyAttributes &metaData) +{ + UpdateValue(thread, entry, value); + SetAttributes(thread, entry, metaData); +} + +void GlobalDictionary::SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData) +{ + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + Set(thread, index, metaData.GetTaggedValue()); +} + +void GlobalDictionary::UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value) +{ + SetValue(thread, entry, value); +} + +void GlobalDictionary::GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const +{ + ASSERT_PRINT(offset + EntriesCount() <= static_cast(keyArray->GetLength()), + "keyArray capacity is not enough for dictionary"); + int arrayIndex = 0; + int size = Size(); + + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyAttributes attr = GetAttributes(hashIndex); + std::pair pair(key, attr.GetOffset()); + sortArr.push_back(pair); + } + } + std::sort(sortArr.begin(), sortArr.end(), CompKey); + for (auto entry : sortArr) { + JSTaggedValue nameKey = entry.first; + keyArray->Set(thread, arrayIndex + offset, nameKey); + arrayIndex++; + } +} + +void GlobalDictionary::GetEnumAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray, + array_size_t *keys) const +{ + ASSERT_PRINT(offset + EntriesCount() <= static_cast(keyArray->GetLength()), + "keyArray capacity is not enough for dictionary"); + int arrayIndex = 0; + int size = Size(); + + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = GetKey(hashIndex); + if (key.IsString()) { + PropertyAttributes attr = GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + std::pair pair(key, attr.GetOffset()); + sortArr.push_back(pair); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), CompKey); + for (const auto &entry : sortArr) { + JSTaggedValue nameKey = entry.first; + keyArray->Set(thread, arrayIndex + offset, nameKey); + arrayIndex++; + } + *keys += arrayIndex; +} + +bool GlobalDictionary::CompKey(const std::pair &a, const std::pair &b) +{ + return a.second < b.second; +} + +void GlobalDictionary::InvalidatePropertyBox(JSThread *thread, const JSHandle &dictHandle, int entry, + const PropertyAttributes &metaData) +{ + PropertyBox *box = dictHandle->GetBox(entry); + PropertyAttributes originAttr = dictHandle->GetAttributes(entry); + PropertyAttributes newAttr(metaData); + + ASSERT(!box->GetValue().IsHole()); + int index = originAttr.GetDictionaryOrder(); + JSHandle oldValue(thread, box->GetValue()); + GlobalDictionary::InvalidateAndReplaceEntry(thread, dictHandle, index, oldValue); + dictHandle->SetAttributes(thread, entry, newAttr); +} + +void GlobalDictionary::InvalidateAndReplaceEntry(JSThread *thread, const JSHandle &dictHandle, + int entry, const JSHandle &oldValue) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + PropertyBox *box = dictHandle->GetBox(entry); + PropertyAttributes attr = dictHandle->GetAttributes(entry); + ASSERT_PRINT(attr.IsConfigurable(), "property must be configurable"); + ASSERT_PRINT(!box->GetValue().IsHole(), "value must not be hole"); + + // Swap with a copy. + JSHandle newBox = factory->NewPropertyBox(oldValue); + // Cell is officially mutable henceforth. + attr.SetBoxType(PropertyBoxType::MUTABLE); + dictHandle->SetAttributes(thread, entry, attr); + dictHandle->UpdateValue(thread, entry, newBox.GetTaggedValue()); + box->Clear(thread); +} +} // namespace ecmascript +} // namespace panda + +#endif diff --git a/ecmascript/global_dictionary.h b/ecmascript/global_dictionary.h new file mode 100644 index 0000000000000000000000000000000000000000..9e36dc6409be076ced1df7b6a97ddfd068b053eb --- /dev/null +++ b/ecmascript/global_dictionary.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_GLOBAL_DICTIONARY_H +#define PANDA_RUNTIME_GLOBAL_DICTIONARY_H + +#include "ecmascript/ecma_string.h" +#include "ecmascript/ic/property_box.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/property_attributes.h" +#include "ecmascript/tagged_hash_table.h" + +namespace panda { +namespace ecmascript { +class GlobalDictionary : public OrderTaggedHashTable { +public: + using OrderHashTableT = OrderTaggedHashTable; + inline static int GetKeyIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + static inline bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &other); + + static inline int Hash(const JSTaggedValue &key); + inline static void InvalidatePropertyBox(JSThread *thread, const JSHandle &dictHandle, int entry, + const PropertyAttributes &metaData); + + inline static void InvalidateAndReplaceEntry(JSThread *thread, const JSHandle &dictHandle, + int entry, const JSHandle &oldValue); + + inline PropertyBox *GetBox(int entry) const; + + inline JSTaggedValue GetValue(int entry) const; + + inline PropertyAttributes GetAttributes(int entry) const; + + inline void SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, + const PropertyAttributes &detail); + + inline void ClearEntry([[maybe_unused]] const JSThread *thread, int entry); + + inline void UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, + const PropertyAttributes &metaData); + + inline void UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value); + + inline void SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData); + + inline void GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const; + + inline void GetEnumAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray, array_size_t *keys) const; + + static bool inline CompKey(const std::pair &a, + const std::pair &b); + + DECL_DUMP() +private: + static constexpr int ENTRY_KEY_INDEX = 0; + static constexpr int ENTRY_VALUE_INDEX = 1; + static constexpr int ENTRY_DETAILS_INDEX = 2; + static constexpr int ENTRY_SIZE = 3; +}; +} // namespace ecmascript +} // namespace panda +#endif diff --git a/ecmascript/global_env.cpp b/ecmascript/global_env.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8840ebff5c9f504c6232ecdaae4888ad3f41ae78 --- /dev/null +++ b/ecmascript/global_env.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecma_module.h" +#include "ecmascript/builtins/builtins_promise_handler.h" +#include "ecmascript/class_linker/program_object-inl.h" +#include "ecmascript/global_env.h" +#include "ecmascript/ic/ic_handler.h" +#include "ecmascript/ic/proto_change_details.h" +#include "ecmascript/jobs/micro_job_queue.h" +#include "ecmascript/js_generator_object.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/object_factory.h" +#include "js_array.h" +#include "js_realm.h" + +namespace panda::ecmascript { +JSHandle GlobalEnv::GetSymbol(JSThread *thread, const JSHandle &string) +{ + JSHandle symbolFunction(GetSymbolFunction()); + return JSObject::GetProperty(thread, symbolFunction, string).GetValue(); +} + +JSHandle GlobalEnv::GetStringPrototypeFunctionByName(JSThread *thread, const char *name) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle stringFuncObj(GetStringFunction()); + JSHandle stringFuncPrototype(thread, stringFuncObj->GetPrototype(thread)); + JSHandle nameKey(factory->NewFromString(name)); + return JSObject::GetProperty(thread, stringFuncPrototype, nameKey).GetValue(); +} + +JSHandle GlobalEnv::GetStringFunctionByName(JSThread *thread, const char *name) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle stringFuncObj = GetStringFunction(); + JSHandle nameKey(factory->NewFromString(name)); + return JSObject::GetProperty(thread, stringFuncObj, nameKey).GetValue(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/global_env.h b/ecmascript/global_env.h new file mode 100644 index 0000000000000000000000000000000000000000..6ea196415bca3bc5c052409a65d7dce24afffa25 --- /dev/null +++ b/ecmascript/global_env.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_GLOBAL_ENV_H +#define PANDA_RUNTIME_ECMASCRIPT_GLOBAL_ENV_H + +#include "ecmascript/global_env_constants-inl.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_global_object.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/lexical_env.h" + +namespace panda::ecmascript { +class JSThread; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_FIELDS(V) \ + /* Function */ \ + V(JSTaggedValue, ObjectFunction, OBJECT_FUNCTION_INDEX) \ + V(JSTaggedValue, ObjectFunctionPrototype, OBJECT_FUNCTION_PROTOTYPE_INDEX) \ + V(JSTaggedValue, ObjectFunctionPrototypeClass, OBJECT_FUNCTION_PROTOTYPE_CLASS_INDEX) \ + V(JSTaggedValue, FunctionFunction, FUNCTION_FUNCTION_INDEX) \ + V(JSTaggedValue, FunctionPrototype, FUNCTION_PROTOTYPE_INDEX) \ + V(JSTaggedValue, NumberFunction, NUMBER_FUNCTION_INDEX) \ + V(JSTaggedValue, DateFunction, DATE_FUNCTION_INDEX) \ + V(JSTaggedValue, BooleanFunction, BOOLEAN_FUNCTION_INDEX) \ + V(JSTaggedValue, ErrorFunction, ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, ArrayFunction, ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, ArrayPrototype, ARRAY_PROTOTYPE_INDEX) \ + V(JSTaggedValue, TypedArrayFunction, TYPED_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, TypedArrayPrototype, TYPED_ARRAY_PROTOTYPE_INDEX) \ + V(JSTaggedValue, Int8ArrayFunction, INT8_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Uint8ArrayFunction, UINT8_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Uint8ClampedArrayFunction, UINT8_CLAMPED_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Int16ArrayFunction, INT16_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Uint16ArrayFunction, UINT16_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Int32ArrayFunction, INT32_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Uint32ArrayFunction, UINT32_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Float32ArrayFunction, FLOAT32_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Float64ArrayFunction, FLOAT64_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, ArrayBufferFunction, ARRAY_BUFFER_FUNCTION_INDEX) \ + V(JSTaggedValue, ArrayProtoValuesFunction, ARRAY_PROTO_VALUES_FUNCTION_INDEX) \ + V(JSTaggedValue, DataViewFunction, DATA_VIEW_FUNCTION_INDEX) \ + V(JSTaggedValue, SymbolFunction, SYMBOL_FUNCTION_INDEX) \ + V(JSTaggedValue, RangeErrorFunction, RANGE_ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, ReferenceErrorFunction, REFERENCE_ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, TypeErrorFunction, TYPE_ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, URIErrorFunction, URI_ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, SyntaxErrorFunction, SYNTAX_ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, EvalErrorFunction, EVAL_ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, IntlFunction, INTL_FUNCTION_INDEX) \ + V(JSTaggedValue, LocaleFunction, LOCALE_FUNCTION_INDEX) \ + V(JSTaggedValue, DateTimeFormatFunction, DATE_TIME_FORMAT_FUNCTION_INDEX) \ + V(JSTaggedValue, RelativeTimeFormatFunction, RELATIVE_TIME_FORMAT_FUNCTION_INDEX) \ + V(JSTaggedValue, NumberFormatFunction, NUMBER_FORMAT_FUNCTION_INDEX) \ + V(JSTaggedValue, CollatorFunction, COLLATOR_FUNCTION_INDEX) \ + V(JSTaggedValue, PluralRulesFunction, PLURAL_RULES_FUNCTION_INDEX) \ + V(JSTaggedValue, RegExpFunction, REGEXP_FUNCTION_INDEX) \ + V(JSTaggedValue, BuiltinsSetFunction, BUILTINS_SET_FUNCTION_INDEX) \ + V(JSTaggedValue, SetPrototype, SET_PROTOTYPE_INDEX) \ + V(JSTaggedValue, BuiltinsMapFunction, BUILTINS_MAP_FUNCTION_INDEX) \ + V(JSTaggedValue, BuiltinsWeakMapFunction, BUILTINS_WEAK_MAP_FUNCTION_INDEX) \ + V(JSTaggedValue, BuiltinsWeakSetFunction, BUILTINS_WEAK_SET_FUNCTION_INDEX) \ + V(JSTaggedValue, MapPrototype, MAP_PROTOTYPE_INDEX) \ + V(JSTaggedValue, MathFunction, MATH_FUNCTION_INDEX) \ + V(JSTaggedValue, JsonFunction, JSON_FUNCTION_INDEX) \ + V(JSTaggedValue, StringFunction, STRING_FUNCTION_INDEX) \ + V(JSTaggedValue, ProxyFunction, PROXY_FUNCTION_INDEX) \ + V(JSTaggedValue, GeneratorFunctionFunction, GENERATOR_FUNCTION_OFFSET) \ + V(JSTaggedValue, GeneratorFunctionPrototype, GENERATOR_FUNCTION_PROTOTYPE_OFFSET) \ + V(JSTaggedValue, InitialGenerator, INITIAL_GENERATOR_OFFSET) \ + V(JSTaggedValue, GeneratorPrototype, GENERATOR_PROTOTYPE_OFFSET) \ + V(JSTaggedValue, ReflectFunction, REFLECT_FUNCTION_INDEX) \ + V(JSTaggedValue, AsyncFunction, ASYNC_FUNCTION_INDEX) \ + V(JSTaggedValue, AsyncFunctionPrototype, ASYNC_FUNCTION_PROTOTYPE_INDEX) \ + V(JSTaggedValue, JSGlobalObject, JS_GLOBAL_OBJECT_INDEX) \ + V(JSTaggedValue, EmptyArray, EMPTY_ARRAY_OBJECT_INDEX) \ + V(JSTaggedValue, EmptyLayoutInfo, EMPTY_LAYOUT_INFO_OBJECT_INDEX) \ + V(JSTaggedValue, EmptyTaggedQueue, EMPTY_TAGGED_QUEUE_OBJECT_INDEX) \ + V(JSTaggedValue, HasInstanceSymbol, HASINSTANCE_SYMBOL_INDEX) \ + V(JSTaggedValue, IsConcatSpreadableSymbol, ISCONCAT_SYMBOL_INDEX) \ + V(JSTaggedValue, ToStringTagSymbol, TOSTRINGTAG_SYMBOL_INDEX) \ + V(JSTaggedValue, IteratorSymbol, ITERATOR_SYMBOL_INDEX) \ + V(JSTaggedValue, MatchSymbol, MATCH_SYMBOL_INDEX) \ + V(JSTaggedValue, ReplaceSymbol, REPLACE_SYMBOL_INDEX) \ + V(JSTaggedValue, SearchSymbol, SEARCH_SYMBOL_INDEX) \ + V(JSTaggedValue, SpeciesSymbol, SPECIES_SYMBOL_INDEX) \ + V(JSTaggedValue, SplitSymbol, SPLIT_SYMBOL_INDEX) \ + V(JSTaggedValue, ToPrimitiveSymbol, TOPRIMITIVE_SYMBOL_INDEX) \ + V(JSTaggedValue, UnscopablesSymbol, UNSCOPABLES_SYMBOL_INDEX) \ + V(JSTaggedValue, HoleySymbol, HOLEY_SYMBOL_OFFSET) \ + V(JSTaggedValue, ElementICSymbol, ELEMENT_IC_SYMBOL_OFFSET) \ + V(JSTaggedValue, IteratorPrototype, ITERATOR_PROTOTYPE_INDEX) \ + V(JSTaggedValue, ForinIteratorPrototype, FORIN_ITERATOR_PROTOTYPE_INDEX) \ + V(JSTaggedValue, ForinIteratorClass, FOR_IN_ITERATOR_CLASS_INDEX) \ + V(JSTaggedValue, StringIterator, STRING_ITERATOR_INDEX) \ + V(JSTaggedValue, MapIteratorPrototype, MAP_ITERATOR_PROTOTYPE_INDEX) \ + V(JSTaggedValue, SetIteratorPrototype, SET_ITERATOR_PROTOTYPE_INDEX) \ + V(JSTaggedValue, ArrayIteratorPrototype, ARRAY_ITERATOR_PROTOTYPE_INDEX) \ + V(JSTaggedValue, StringIteratorPrototype, STRING_ITERATOR_PROTOTYPE_INDEX) \ + /* SymbolTable *RegisterSymbols */ \ + V(JSTaggedValue, RegisterSymbols, SYMBOLS_INDEX) \ + V(JSTaggedValue, ThrowTypeError, THROW_TYPE_ERROR_INDEX) \ + V(JSTaggedValue, PromiseFunction, PROMISE_FUNCTION_INDEX) \ + V(JSTaggedValue, PromiseReactionJob, PROMISE_REACTION_JOB_INDEX) \ + V(JSTaggedValue, PromiseResolveThenableJob, PROMISE_REACTION_THENABLE_JOB_INDEX) \ + V(JSTaggedValue, TemplateMap, TEMPLATE_MAP_INDEX) \ + V(JSTaggedValue, FunctionClassWithProto, FUNCTION_CLASS_WITH_PROTO) \ + V(JSTaggedValue, FunctionClassWithoutProto, FUNCTION_CLASS_WITHOUT_PROTO) \ + V(JSTaggedValue, FunctionClassWithoutName, FUNCTION_CLASS_WITHOUT_NAME) \ + V(JSTaggedValue, ArgumentsClass, ARGUMENTS_CLASS) \ + V(JSTaggedValue, ArgumentsCallerAccessor, ARGUMENTSCALLERACCESSOR) \ + V(JSTaggedValue, ArgumentsCalleeAccessor, ARGUMENTSCALLEEACCESSOR) \ + V(JSTaggedValue, AsyncFunctionClass, ASYNC_FUNCTION_CLASS) \ + V(JSTaggedValue, AsyncAwaitStatusFunctionClass, ASYNC_AWAIT_STATUS_FUNCTION_CLASS) \ + V(JSTaggedValue, PromiseReactionFunctionClass, PROMISE_REACTION_FUNCTION_CLASS) \ + V(JSTaggedValue, PromiseExecutorFunctionClass, PROMISE_EXECUTOR_FUNCTION_CLASS) \ + V(JSTaggedValue, GeneratorFunctionClass, GENERATOR_FUNCTION_CLASS) \ + V(JSTaggedValue, PromiseAllResolveElementFunctionClass, PROMISE_ALL_RESOLVE_ELEMENT_FUNC_CLASS) \ + V(JSTaggedValue, ProxyRevocFunctionClass, PROXY_REVOC_FUNCTION_CLASS) \ + V(JSTaggedValue, NativeErrorFunctionClass, NATIVE_ERROR_FUNCTION_CLASS) \ + V(JSTaggedValue, SpecificTypedArrayFunctionClass, SPERCIFIC_TYPED_ARRAY_FUNCTION_CLASS) \ + V(JSTaggedValue, ConstructorFunctionClass, CONSTRUCTOR_FUNCTION_CLASS) \ + V(JSTaggedValue, NormalFunctionClass, NORMAL_FUNCTION_CLASS) \ + V(JSTaggedValue, JSIntlBoundFunctionClass, JS_INTL_BOUND_FUNCTION_CLASS) \ + V(JSTaggedValue, NumberFormatLocales, NUMBER_FORMAT_LOCALES_INDEX) \ + V(JSTaggedValue, DateTimeFormatLocales, DATE_TIMEFORMAT_LOCALES_INDEX) \ + V(JSTaggedValue, JSNativeObjectClass, JS_NATIVE_OBJECT_CLASS) + +class GlobalEnv : public TaggedObject { +public: + JSTaggedValue GetGlobalObject() const + { + return GetJSGlobalObject().GetTaggedValue(); + } + + void InitGlobalObject(); + + static GlobalEnv *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSGlobalEnv()); + return reinterpret_cast(object); + } + + JSHandle GetSymbol(JSThread *thread, const JSHandle &string); + JSHandle GetStringFunctionByName(JSThread *thread, const char *name); + JSHandle GetStringPrototypeFunctionByName(JSThread *thread, const char *name); + + enum Field { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_SLOT(type, name, index) index, + GLOBAL_ENV_FIELDS(GLOBAL_ENV_SLOT) +#undef GLOBAL_ENV_SLOT + FINAL_INDEX + }; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_FIELD_ACCESSORS(type, name, index) \ + inline JSHandle Get##name() const \ + { \ + const uintptr_t address = \ + reinterpret_cast(this) + HEADER_SIZE + index * JSTaggedValue::TaggedTypeSize(); \ + JSHandle result(address); \ + return result; \ + } \ + template \ + inline void Set##name(const JSThread *thread, JSHandle value, BarrierMode mode = WRITE_BARRIER) \ + { \ + int offset = HEADER_SIZE + index * JSTaggedValue::TaggedTypeSize(); \ + if (mode == WRITE_BARRIER && value.GetTaggedValue().IsHeapObject()) { \ + Barriers::SetDynObject(thread, this, offset, value.GetTaggedValue().GetRawData()); \ + } else { \ + Barriers::SetDynPrimitive(this, offset, value.GetTaggedValue().GetRawData()); \ + } \ + } \ + inline void Set##name(const JSThread *thread, type value, BarrierMode mode = WRITE_BARRIER) \ + { \ + int offset = HEADER_SIZE + index * JSTaggedValue::TaggedTypeSize(); \ + if (mode == WRITE_BARRIER && value.IsHeapObject()) { \ + Barriers::SetDynObject(thread, this, offset, value.GetRawData()); \ + } else { \ + Barriers::SetDynPrimitive(this, offset, value.GetRawData()); \ + } \ + } + GLOBAL_ENV_FIELDS(GLOBAL_ENV_FIELD_ACCESSORS) +#undef GLOBAL_ENV_FIELD_ACCESSORS + + static constexpr size_t HEADER_SIZE = sizeof(TaggedObject); + static constexpr size_t SIZE = HEADER_SIZE + FINAL_INDEX * JSTaggedValue::TaggedTypeSize(); + + DECL_DUMP() + + inline void Visitor(const EcmaObjectRangeVisitor &v) + { + v(this, ObjectSlot(ToUintPtr(this) + HEADER_SIZE), ObjectSlot(ToUintPtr(this) + SIZE)); + } +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_GLOBAL_ENV_H diff --git a/ecmascript/global_env_constants-inl.h b/ecmascript/global_env_constants-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..c937cffb59d9b086a33f8587d7f8ecb3bbe6df09 --- /dev/null +++ b/ecmascript/global_env_constants-inl.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_ECMASCRIPT_GLOBAL_ENV_CONSTANTS_INL_H +#define RUNTIME_ECMASCRIPT_GLOBAL_ENV_CONSTANTS_INL_H + +#include "ecmascript/global_env_constants.h" +#include "ecmascript/ecma_macros.h" + +namespace panda::ecmascript { +inline const JSTaggedValue *GlobalEnvConstants::BeginSlot() const +{ + return &constants_[static_cast(ConstantIndex::CONSTATNT_BEGIN)]; +} + +inline const JSTaggedValue *GlobalEnvConstants::EndSlot() const +{ + return &constants_[static_cast(ConstantIndex::CONSTATNT_END)]; +} + +inline void GlobalEnvConstants::SetConstant(ConstantIndex index, JSTaggedValue value) +{ + DASSERT_PRINT(index >= ConstantIndex::CONSTATNT_BEGIN && index < ConstantIndex::CONSTATNT_END, + "Root Index out of bound"); + constants_[static_cast(index)] = value; +} + +// clang-format off +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECL_GET_IMPL(Type, Name, Index, Desc) \ + inline const Type GlobalEnvConstants::Get##Name() const \ + { \ + return constants_[static_cast(ConstantIndex::Index)]; \ + } \ + inline const JSHandle GlobalEnvConstants::GetHandled##Name() const \ + { \ + return JSHandle(reinterpret_cast( \ + &constants_[static_cast(ConstantIndex::Index)])); \ + } + + GLOBAL_ENV_CONSTANT_CLASS(DECL_GET_IMPL) // NOLINT(readability-const-return-type) + GLOBAL_ENV_CONSTANT_SPECIAL(DECL_GET_IMPL) // NOLINT(readability-const-return-type) + GLOBAL_ENV_CONSTANT_CONSTANT(DECL_GET_IMPL) // NOLINT(readability-const-return-type) + GLOBAL_ENV_CONSTANT_ACCESSOR(DECL_GET_IMPL) // NOLINT(readability-const-return-type) +#undef DECL_GET_IMPL +// clang-format on +} // namespace panda::ecmascript +#endif // RUNTIME_ECMASCRIPT_GLOBAL_ENV_CONSTANTS_INL_H diff --git a/ecmascript/global_env_constants.cpp b/ecmascript/global_env_constants.cpp new file mode 100644 index 0000000000000000000000000000000000000000..88266834fa66a89162d5c922001184ec3d4bdfca --- /dev/null +++ b/ecmascript/global_env_constants.cpp @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/global_env_constants.h" + +#include "ecma_vm.h" +#include "ecmascript/accessor_data.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_module.h" +#include "ecmascript/builtins.h" +#include "ecmascript/builtins/builtins_global.h" +#include "ecmascript/class_linker/program_object.h" +#include "ecmascript/free_object.h" +#include "ecmascript/global_env.h" +#include "ecmascript/global_env_constants-inl.h" +#include "ecmascript/ic/ic_handler.h" +#include "ecmascript/ic/property_box.h" +#include "ecmascript/ic/proto_change_details.h" +#include "ecmascript/jobs/micro_job_queue.h" +#include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_arguments.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_for_in_iterator.h" +#include "ecmascript/js_generator_object.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/js_map.h" +#include "ecmascript/js_map_iterator.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_proxy.h" +#include "ecmascript/js_realm.h" +#include "ecmascript/js_regexp.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/js_symbol.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" + + +namespace panda::ecmascript { +void GlobalEnvConstants::InitRootsClass([[maybe_unused]] JSThread *thread, JSHClass *dynClassClass) +{ + // Global constants are readonly. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + SetConstant(ConstantIndex::HCLASS_CLASS_INDEX, JSTaggedValue(dynClassClass)); + SetConstant(ConstantIndex::STRING_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, 0, JSType::STRING).GetTaggedValue()); + SetConstant(ConstantIndex::ARRAY_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, 0, JSType::TAGGED_ARRAY).GetTaggedValue()); + SetConstant(ConstantIndex::DICTIONARY_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, 0, JSType::TAGGED_DICTIONARY).GetTaggedValue()); + SetConstant( + ConstantIndex::JS_NATIVE_POINTER_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, sizeof(JSNativePointer), JSType::JS_NATIVE_POINTER).GetTaggedValue()); + SetConstant(ConstantIndex::ENV_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, 0, JSType::TAGGED_ARRAY).GetTaggedValue()); + SetConstant(ConstantIndex::FREE_OBJECT_WITH_NONE_FIELD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, FreeObject::NEXT_OFFSET, JSType::FREE_OBJECT_WITH_NONE_FIELD) + .GetTaggedValue()); + SetConstant(ConstantIndex::FREE_OBJECT_WITH_ONE_FIELD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, FreeObject::SIZE_OFFSET, JSType::FREE_OBJECT_WITH_ONE_FIELD) + .GetTaggedValue()); + SetConstant( + ConstantIndex::FREE_OBJECT_WITH_TWO_FIELD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, FreeObject::SIZE, JSType::FREE_OBJECT_WITH_TWO_FIELD).GetTaggedValue()); + SetConstant(ConstantIndex::SYMBOL_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, JSSymbol::SIZE, JSType::SYMBOL).GetTaggedValue()); + SetConstant(ConstantIndex::ACCESSOE_DATA_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, AccessorData::SIZE, JSType::ACCESSOR_DATA).GetTaggedValue()); + SetConstant( + ConstantIndex::INTERNAL_ACCESSOR_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, AccessorData::SIZE, JSType::INTERNAL_ACCESSOR).GetTaggedValue()); + SetConstant(ConstantIndex::JS_PROXY_ORDINARY_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, JSProxy::SIZE, JSType::JS_PROXY).GetTaggedValue()); + SetConstant(ConstantIndex::OBJECT_WRAPPER_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, ObjectWrapper::SIZE, JSType::OBJECT_WRAPPER).GetTaggedValue()); + SetConstant( + ConstantIndex::COMPLETION_RECORD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, CompletionRecord::SIZE, JSType::COMPLETION_RECORD).GetTaggedValue()); + SetConstant( + ConstantIndex::GENERATOR_CONTEST_INDEX, + factory->NewEcmaDynClass(dynClassClass, GeneratorContext::SIZE, JSType::JS_GENERATOR_CONTEXT).GetTaggedValue()); + SetConstant( + ConstantIndex::CAPABILITY_RECORD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, PromiseCapability::SIZE, JSType::PROMISE_CAPABILITY).GetTaggedValue()); + SetConstant( + ConstantIndex::REACTIONS_RECORD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, PromiseReaction::SIZE, JSType::PROMISE_REACTIONS).GetTaggedValue()); + SetConstant(ConstantIndex::PROMISE_ITERATOR_RECORD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, PromiseIteratorRecord::SIZE, JSType::PROMISE_ITERATOR_RECORD) + .GetTaggedValue()); + SetConstant(ConstantIndex::PROMISE_RECORD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, PromiseRecord::SIZE, JSType::PROMISE_RECORD).GetTaggedValue()); + SetConstant( + ConstantIndex::PROMISE_RESOLVING_FUNCTIONS_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, ResolvingFunctionsRecord::SIZE, JSType::RESOLVING_FUNCTIONS_RECORD) + .GetTaggedValue()); + SetConstant( + ConstantIndex::MICRO_JOB_QUEUE_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, job::MicroJobQueue::SIZE, JSType::MICRO_JOB_QUEUE).GetTaggedValue()); + SetConstant(ConstantIndex::PENDING_JOB_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, job::PendingJob::SIZE, JSType::PENDING_JOB).GetTaggedValue()); + SetConstant( + ConstantIndex::PROTO_CHANGE_MARKER_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, ProtoChangeMarker::SIZE, JSType::PROTO_CHANGE_MARKER).GetTaggedValue()); + SetConstant( + ConstantIndex::PROTO_CHANGE_DETAILS_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, ProtoChangeDetails::SIZE, JSType::PROTOTYPE_INFO).GetTaggedValue()); + SetConstant( + ConstantIndex::PROTOTYPE_HANDLER_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, PrototypeHandler::SIZE, JSType::PROTOTYPE_HANDLER).GetTaggedValue()); + SetConstant( + ConstantIndex::TRANSITION_HANDLER_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, TransitionHandler::SIZE, JSType::TRANSITION_HANDLER).GetTaggedValue()); + SetConstant(ConstantIndex::PROPERTY_BOX_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, PropertyBox::SIZE, JSType::PROPERTY_BOX).GetTaggedValue()); + SetConstant(ConstantIndex::FUNCTION_EXTRA_INFO_INDEX, + factory->NewEcmaDynClass(dynClassClass, JSFunctionExtraInfo::SIZE, JSType::FUNCTION_EXTRA_INFO) + .GetTaggedValue()); + SetConstant(ConstantIndex::PROGRAM_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, Program::SIZE, JSType::PROGRAM).GetTaggedValue()); + SetConstant(ConstantIndex::ECMA_MODULE_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, EcmaModule::SIZE, JSType::ECMA_MODULE).GetTaggedValue()); + + JSHClass *jsProxyCallableClass = *factory->NewEcmaDynClass(dynClassClass, JSProxy::SIZE, JSType::JS_PROXY); + + jsProxyCallableClass->SetCallable(true); + SetConstant(ConstantIndex::JS_PROXY_CALLABLE_CLASS_INDEX, JSTaggedValue(jsProxyCallableClass)); + + JSHClass *jsProxyConstructClass = *factory->NewEcmaDynClass(dynClassClass, JSProxy::SIZE, JSType::JS_PROXY); + + jsProxyConstructClass->SetCallable(true); + jsProxyConstructClass->SetConstructor(true); + SetConstant(ConstantIndex::JS_PROXY_CONSTRUCT_CLASS_INDEX, JSTaggedValue(jsProxyConstructClass)); + + SetConstant(ConstantIndex::JS_REALM_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, JSRealm::SIZE, JSType::JS_REALM).GetTaggedValue()); +} + +// NOLINTNEXTLINE(readability-function-size) +void GlobalEnvConstants::InitGlobalConstant(JSThread *thread) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // SPECIAL INIT + SetConstant(ConstantIndex::UNDEFINED_INDEX, JSTaggedValue::Undefined()); + SetConstant(ConstantIndex::NULL_INDEX, JSTaggedValue::Null()); + auto vm = thread->GetEcmaVM(); + SetConstant(ConstantIndex::EMPTY_STRING_OBJECT_INDEX, JSTaggedValue(EcmaString::CreateEmptyString(vm))); + [[maybe_unused]] auto test = EcmaString::Cast(GetHandledEmptyString().GetObject()); + SetConstant(ConstantIndex::CONSTRUCTOR_STRING_INDEX, factory->NewFromString("constructor").GetTaggedValue()); + SetConstant(ConstantIndex::PROTOTYPE_STRING_INDEX, factory->NewFromString("prototype").GetTaggedValue()); + SetConstant(ConstantIndex::LENGTH_STRING_INDEX, factory->NewFromString("length").GetTaggedValue()); + SetConstant(ConstantIndex::VALUE_STRING_INDEX, factory->NewFromString("value").GetTaggedValue()); + SetConstant(ConstantIndex::SET_STRING_INDEX, factory->NewFromString("set").GetTaggedValue()); + SetConstant(ConstantIndex::GET_STRING_INDEX, factory->NewFromString("get").GetTaggedValue()); + SetConstant(ConstantIndex::WRITABLE_STRING_INDEX, factory->NewFromString("writable").GetTaggedValue()); + SetConstant(ConstantIndex::ENUMERABLE_STRING_INDEX, factory->NewFromString("enumerable").GetTaggedValue()); + SetConstant(ConstantIndex::CONFIGURABLE_STRING_INDEX, factory->NewFromString("configurable").GetTaggedValue()); + /* SymbolTable *RegisterSymbols */ + SetConstant(ConstantIndex::NAME_STRING_INDEX, factory->NewFromString("name").GetTaggedValue()); + SetConstant(ConstantIndex::GETPROTOTYPEOF_STRING_INDEX, factory->NewFromString("getPrototypeOf").GetTaggedValue()); + SetConstant(ConstantIndex::SETPROTOTYPEOF_STRING_INDEX, factory->NewFromString("setPrototypeOf").GetTaggedValue()); + SetConstant(ConstantIndex::ISEXTENSIBLE_STRING_INDEX, factory->NewFromString("isExtensible").GetTaggedValue()); + SetConstant(ConstantIndex::PREVENTEXTENSIONS_STRING_INDEX, + factory->NewFromString("preventExtensions").GetTaggedValue()); + SetConstant(ConstantIndex::GETOWNPROPERTYDESCRIPTOR_STRING_INDEX, + factory->NewFromString("getOwnPropertyDescriptor").GetTaggedValue()); + SetConstant(ConstantIndex::DEFINEPROPERTY_STRING_INDEX, factory->NewFromString("defineProperty").GetTaggedValue()); + SetConstant(ConstantIndex::HAS_STRING_INDEX, factory->NewFromString("has").GetTaggedValue()); + SetConstant(ConstantIndex::DELETEPROPERTY_STRING_INDEX, factory->NewFromString("deleteProperty").GetTaggedValue()); + SetConstant(ConstantIndex::ENUMERATE_STRING_INDEX, factory->NewFromString("enumerate").GetTaggedValue()); + SetConstant(ConstantIndex::OWNKEYS_STRING_INDEX, factory->NewFromString("ownKeys").GetTaggedValue()); + SetConstant(ConstantIndex::APPLY_STRING_INDEX, factory->NewFromString("apply").GetTaggedValue()); + SetConstant(ConstantIndex::NEGATIVE_ZERO_STRING_INDEX, factory->NewFromString("-0").GetTaggedValue()); + SetConstant(ConstantIndex::DONE_STRING_INDEX, factory->NewFromString("done").GetTaggedValue()); + SetConstant(ConstantIndex::PROXY_STRING_INDEX, factory->NewFromString("proxy").GetTaggedValue()); + SetConstant(ConstantIndex::REVOKE_STRING_INDEX, factory->NewFromString("revoke").GetTaggedValue()); + SetConstant(ConstantIndex::NEXT_STRING_INDEX, factory->NewFromString("next").GetTaggedValue()); + SetConstant(ConstantIndex::TO_STRING_STRING_INDEX, factory->NewFromString("toString").GetTaggedValue()); + SetConstant(ConstantIndex::TO_LOCALE_STRING_STRING_INDEX, + factory->NewFromString("toLocaleString").GetTaggedValue()); + SetConstant(ConstantIndex::VALUE_OF_STRING_INDEX, factory->NewFromString("valueOf").GetTaggedValue()); + SetConstant(ConstantIndex::UNDEFINED_STRING_INDEX, factory->NewFromString("undefined").GetTaggedValue()); + SetConstant(ConstantIndex::NULL_STRING_INDEX, factory->NewFromString("null").GetTaggedValue()); + SetConstant(ConstantIndex::BOOLEAN_STRING_INDEX, factory->NewFromString("boolean").GetTaggedValue()); + SetConstant(ConstantIndex::NUMBER_STRING_INDEX, factory->NewFromString("number").GetTaggedValue()); + SetConstant(ConstantIndex::FUNCTION_STRING_INDEX, factory->NewFromString("function").GetTaggedValue()); + SetConstant(ConstantIndex::STRING_STRING_INDEX, factory->NewFromString("string").GetTaggedValue()); + SetConstant(ConstantIndex::SYMBOL_STRING_INDEX, factory->NewFromString("symbol").GetTaggedValue()); + SetConstant(ConstantIndex::OBJECT_STRING_INDEX, factory->NewFromString("object").GetTaggedValue()); + SetConstant(ConstantIndex::TRUE_STRING_INDEX, factory->NewFromString("true").GetTaggedValue()); + SetConstant(ConstantIndex::FALSE_STRING_INDEX, factory->NewFromString("false").GetTaggedValue()); + SetConstant(ConstantIndex::RETURN_STRING_INDEX, factory->NewFromString("return").GetTaggedValue()); + SetConstant(ConstantIndex::PROXY_CONSTRUCT_STRING_INDEX, factory->NewFromString("construct").GetTaggedValue()); + SetConstant(ConstantIndex::PROXY_CALL_STRING_INDEX, factory->NewFromString("call").GetTaggedValue()); + SetConstant(ConstantIndex::PROMISE_THEN_STRING_INDEX, factory->NewFromString("then").GetTaggedValue()); + SetConstant(ConstantIndex::PROMISE_CATCH_STRING_INDEX, factory->NewFromString("catch").GetTaggedValue()); + SetConstant(ConstantIndex::SCRIPT_JOB_STRING_INDEX, factory->NewFromString("ScriptJobs").GetTaggedValue()); + SetConstant(ConstantIndex::PROMISE_STRING_INDEX, factory->NewFromString("PrimiseJobs").GetTaggedValue()); + SetConstant(ConstantIndex::THROWER_STRING_INDEX, factory->NewFromString("Thrower").GetTaggedValue()); + SetConstant(ConstantIndex::IDENTITY_STRING_INDEX, factory->NewFromString("Identity").GetTaggedValue()); + SetConstant(ConstantIndex::CALLER_STRING_INDEX, factory->NewFromString("caller").GetTaggedValue()); + SetConstant(ConstantIndex::CALLEE_STRING_INDEX, factory->NewFromString("callee").GetTaggedValue()); + SetConstant(ConstantIndex::INT8_ARRAY_STRING_INDEX, factory->NewFromString("Int8Array").GetTaggedValue()); + SetConstant(ConstantIndex::UINT8_ARRAY_STRING_INDEX, factory->NewFromString("Uint8Array").GetTaggedValue()); + SetConstant(ConstantIndex::UINT8_CLAMPED_ARRAY_STRING_INDEX, + factory->NewFromString("Uint8ClampedArray").GetTaggedValue()); + SetConstant(ConstantIndex::INT16_ARRAY_STRING_INDEX, factory->NewFromString("Int16Array").GetTaggedValue()); + SetConstant(ConstantIndex::UINT16_ARRAY_STRING_INDEX, factory->NewFromString("Uint16Array").GetTaggedValue()); + SetConstant(ConstantIndex::INT32_ARRAY_STRING_INDEX, factory->NewFromString("Int32Array").GetTaggedValue()); + SetConstant(ConstantIndex::UINT32_ARRAY_STRING_INDEX, factory->NewFromString("Uint32Array").GetTaggedValue()); + SetConstant(ConstantIndex::FLOAT32_ARRAY_STRING_INDEX, factory->NewFromString("Float32Array").GetTaggedValue()); + SetConstant(ConstantIndex::FLOAT64_ARRAY_STRING_INDEX, factory->NewFromString("Float64Array").GetTaggedValue()); + SetConstant(ConstantIndex::ASYNC_FUNCTION_STRING_INDEX, factory->NewFromString("AsyncFunction").GetTaggedValue()); + SetConstant(ConstantIndex::PROMISE_RESOLVE_STRING_INDEX, factory->NewFromString("resolve").GetTaggedValue()); + SetConstant(ConstantIndex::ID_STRING_INDEX, factory->NewFromString("id").GetTaggedValue()); + SetConstant(ConstantIndex::METHOD_STRING_INDEX, factory->NewFromString("method").GetTaggedValue()); + SetConstant(ConstantIndex::PARAMS_STRING_INDEX, factory->NewFromString("params").GetTaggedValue()); + SetConstant(ConstantIndex::RESULT_STRING_INDEX, factory->NewFromString("result").GetTaggedValue()); + SetConstant(ConstantIndex::TO_JSON_STRING_INDEX, factory->NewFromString("toJSON").GetTaggedValue()); + SetConstant(ConstantIndex::GLOBAL_STRING_INDEX, factory->NewFromString("global").GetTaggedValue()); + SetConstant(ConstantIndex::MESSAGE_STRING_INDEX, factory->NewFromString("message").GetTaggedValue()); + SetConstant(ConstantIndex::ERROR_STRING_INDEX, factory->NewFromString("Error").GetTaggedValue()); + SetConstant(ConstantIndex::RANGE_ERROR_STRING_INDEX, factory->NewFromString("RangeError").GetTaggedValue()); + SetConstant(ConstantIndex::REFERENCE_ERROR_STRING_INDEX, factory->NewFromString("ReferenceError").GetTaggedValue()); + SetConstant(ConstantIndex::TYPE_ERROR_STRING_INDEX, factory->NewFromString("TypeError").GetTaggedValue()); + SetConstant(ConstantIndex::URI_ERROR_STRING_INDEX, factory->NewFromString("URIError").GetTaggedValue()); + SetConstant(ConstantIndex::SYNTAX_ERROR_STRING_INDEX, factory->NewFromString("SyntaxError").GetTaggedValue()); + SetConstant(ConstantIndex::EVAL_ERROR_STRING_INDEX, factory->NewFromString("EvalError").GetTaggedValue()); + SetConstant(ConstantIndex::STACK_STRING_INDEX, factory->NewFromString("stack").GetTaggedValue()); + SetConstant(ConstantIndex::STACK_EMPTY_STRING_INDEX, factory->NewFromString("stackisempty").GetTaggedValue()); + SetConstant(ConstantIndex::OBJ_NOT_COERCIBLE_STRING_INDEX, + factory->NewFromString("objectnotcoercible").GetTaggedValue()); + /* for Intl. */ + SetConstant(ConstantIndex::LANGUAGE_STRING_CLASS_INDEX, factory->NewFromString("language").GetTaggedValue()); + SetConstant(ConstantIndex::SCRIPT_STRING_CLASS_INDEX, factory->NewFromString("script").GetTaggedValue()); + SetConstant(ConstantIndex::REGION_STRING_CLASS_INDEX, factory->NewFromString("region").GetTaggedValue()); + SetConstant(ConstantIndex::BASE_NAME_STRING_CLASS_INDEX, factory->NewFromString("baseName").GetTaggedValue()); + SetConstant(ConstantIndex::CALENDAR_STRING_CLASS_INDEX, factory->NewFromString("calendar").GetTaggedValue()); + SetConstant(ConstantIndex::COLLATION_STRING_CLASS_INDEX, factory->NewFromString("collation").GetTaggedValue()); + SetConstant(ConstantIndex::HOUR_CYCLE_STRING_CLASS_INDEX, factory->NewFromString("hourCycle").GetTaggedValue()); + SetConstant(ConstantIndex::CASE_FIRST_STRING_CLASS_INDEX, factory->NewFromString("caseFirst").GetTaggedValue()); + SetConstant(ConstantIndex::NUMERIC_STRING_CLASS_INDEX, factory->NewFromString("numeric").GetTaggedValue()); + SetConstant(ConstantIndex::NUMBERING_SYSTEM_STRING_CLASS_INDEX, + factory->NewFromString("numberingSystem").GetTaggedValue()); + SetConstant(ConstantIndex::TYPE_STRING_INDEX, factory->NewFromString("type").GetTaggedValue()); + SetConstant(ConstantIndex::LOCALE_MATCHER_STRING_INDEX, factory->NewFromString("localeMatcher").GetTaggedValue()); + SetConstant(ConstantIndex::FORMAT_MATCHER_STRING_INDEX, factory->NewFromString("formatMatcher").GetTaggedValue()); + SetConstant(ConstantIndex::HOUR12_STRING_INDEX, factory->NewFromString("hour12").GetTaggedValue()); + SetConstant(ConstantIndex::H11_STRING_INDEX, factory->NewFromString("h11").GetTaggedValue()); + SetConstant(ConstantIndex::H12_STRING_INDEX, factory->NewFromString("h12").GetTaggedValue()); + SetConstant(ConstantIndex::H23_STRING_INDEX, factory->NewFromString("h23").GetTaggedValue()); + SetConstant(ConstantIndex::H24_STRING_INDEX, factory->NewFromString("h24").GetTaggedValue()); + SetConstant(ConstantIndex::WEEK_DAY_STRING_INDEX, factory->NewFromString("weekday").GetTaggedValue()); + SetConstant(ConstantIndex::ERA_STRING_INDEX, factory->NewFromString("era").GetTaggedValue()); + SetConstant(ConstantIndex::YEAR_STRING_INDEX, factory->NewFromString("year").GetTaggedValue()); + SetConstant(ConstantIndex::QUARTER_STRING_INDEX, factory->NewFromString("quarter").GetTaggedValue()); + SetConstant(ConstantIndex::MONTH_STRING_INDEX, factory->NewFromString("month").GetTaggedValue()); + SetConstant(ConstantIndex::DAY_STRING_INDEX, factory->NewFromString("day").GetTaggedValue()); + SetConstant(ConstantIndex::HOUR_STRING_INDEX, factory->NewFromString("hour").GetTaggedValue()); + SetConstant(ConstantIndex::MINUTE_STRING_INDEX, factory->NewFromString("minute").GetTaggedValue()); + SetConstant(ConstantIndex::SECOND_STRING_INDEX, factory->NewFromString("second").GetTaggedValue()); + SetConstant(ConstantIndex::YEARS_STRING_INDEX, factory->NewFromString("years").GetTaggedValue()); + SetConstant(ConstantIndex::QUARTERS_STRING_INDEX, factory->NewFromString("quarters").GetTaggedValue()); + SetConstant(ConstantIndex::MONTHS_STRING_INDEX, factory->NewFromString("months").GetTaggedValue()); + SetConstant(ConstantIndex::DAYS_STRING_INDEX, factory->NewFromString("days").GetTaggedValue()); + SetConstant(ConstantIndex::HOURS_STRING_INDEX, factory->NewFromString("hours").GetTaggedValue()); + SetConstant(ConstantIndex::MINUTES_STRING_INDEX, factory->NewFromString("minutes").GetTaggedValue()); + SetConstant(ConstantIndex::SECONDS_STRING_INDEX, factory->NewFromString("seconds").GetTaggedValue()); + SetConstant(ConstantIndex::TIME_ZONE_NAME_STRING_INDEX, factory->NewFromString("timeZoneName").GetTaggedValue()); + SetConstant(ConstantIndex::LOCALE_STRING_INDEX, factory->NewFromString("locale").GetTaggedValue()); + SetConstant(ConstantIndex::TIME_ZONE_STRING_INDEX, factory->NewFromString("timeZone").GetTaggedValue()); + SetConstant(ConstantIndex::LITERAL_STRING_INDEX, factory->NewFromString("literal").GetTaggedValue()); + SetConstant(ConstantIndex::YEAR_NAME_STRING_INDEX, factory->NewFromString("yearName").GetTaggedValue()); + SetConstant(ConstantIndex::DAY_PERIOD_STRING_INDEX, factory->NewFromString("dayPeriod").GetTaggedValue()); + SetConstant(ConstantIndex::FRACTIONAL_SECOND_DIGITS_STRING_INDEX, + factory->NewFromString("fractionalSecondDigits").GetTaggedValue()); + SetConstant(ConstantIndex::FRACTIONAL_SECOND_STRING_INDEX, + factory->NewFromString("fractionalSecond").GetTaggedValue()); + SetConstant(ConstantIndex::RELATED_YEAR_STRING_INDEX, factory->NewFromString("relatedYear").GetTaggedValue()); + SetConstant(ConstantIndex::LOOK_UP_STRING_INDEX, factory->NewFromString("lookup").GetTaggedValue()); + SetConstant(ConstantIndex::BEST_FIT_STRING_INDEX, factory->NewFromString("bestfit").GetTaggedValue()); + SetConstant(ConstantIndex::DATE_STYLE_STRING_INDEX, factory->NewFromString("dateStyle").GetTaggedValue()); + SetConstant(ConstantIndex::TIME_STYLE_STRING_INDEX, factory->NewFromString("timeStyle").GetTaggedValue()); + SetConstant(ConstantIndex::UTC_STRING_INDEX, factory->NewFromString("UTC").GetTaggedValue()); + SetConstant(ConstantIndex::INITIALIZED_RELATIVE_INDEX, factory->NewFromString("true").GetTaggedValue()); + SetConstant(ConstantIndex::WEEK_STRING_INDEX, factory->NewFromString("week").GetTaggedValue()); + SetConstant(ConstantIndex::WEEKS_STRING_INDEX, factory->NewFromString("weeks").GetTaggedValue()); + SetConstant(ConstantIndex::SOURCE_STRING_INDEX, factory->NewFromString("source").GetTaggedValue()); + SetConstant(ConstantIndex::FORMAT_STRING_INDEX, factory->NewFromString("format").GetTaggedValue()); + SetConstant(ConstantIndex::EN_US_STRING_INDEX, factory->NewFromString("en-US").GetTaggedValue()); + SetConstant(ConstantIndex::UND_STRING_INDEX, factory->NewFromString("und").GetTaggedValue()); + SetConstant(ConstantIndex::LATN_STRING_INDEX, factory->NewFromString("latn").GetTaggedValue()); + SetConstant(ConstantIndex::STYLE_STRING_INDEX, factory->NewFromString("style").GetTaggedValue()); + SetConstant(ConstantIndex::UNIT_STRING_INDEX, factory->NewFromString("unit").GetTaggedValue()); + SetConstant(ConstantIndex::INTEGER_STRING_INDEX, factory->NewFromString("integer").GetTaggedValue()); + SetConstant(ConstantIndex::NAN_STRING_INDEX, factory->NewFromString("nan").GetTaggedValue()); + SetConstant(ConstantIndex::INFINITY_STRING_INDEX, factory->NewFromString("infinity").GetTaggedValue()); + SetConstant(ConstantIndex::FRACTION_STRING_INDEX, factory->NewFromString("fraction").GetTaggedValue()); + SetConstant(ConstantIndex::DECIMAL_STRING_INDEX, factory->NewFromString("decimal").GetTaggedValue()); + SetConstant(ConstantIndex::GROUP_STRING_INDEX, factory->NewFromString("group").GetTaggedValue()); + SetConstant(ConstantIndex::CURRENCY_STRING_INDEX, factory->NewFromString("currency").GetTaggedValue()); + SetConstant(ConstantIndex::CURRENCY_SIGN_STRING_INDEX, factory->NewFromString("currencySign").GetTaggedValue()); + SetConstant(ConstantIndex::CURRENCY_DISPLAY_STRING_INDEX, + factory->NewFromString("currencyDisplay").GetTaggedValue()); + SetConstant(ConstantIndex::PERCENT_SIGN_STRING_INDEX, factory->NewFromString("percentSign").GetTaggedValue()); + SetConstant(ConstantIndex::PERCENT_STRING_INDEX, factory->NewFromString("percent").GetTaggedValue()); + SetConstant(ConstantIndex::MINUS_SIGN_STRING_INDEX, factory->NewFromString("minusSign").GetTaggedValue()); + SetConstant(ConstantIndex::PLUS_SIGN_STRING_INDEX, factory->NewFromString("plusSign").GetTaggedValue()); + SetConstant(ConstantIndex::EXPONENT_SEPARATOR_STRING_INDEX, + factory->NewFromString("exponentSeparator").GetTaggedValue()); + SetConstant(ConstantIndex::EXPONENT_MINUS_SIGN_INDEX, factory->NewFromString("exponentMinusSign").GetTaggedValue()); + SetConstant(ConstantIndex::EXPONENT_INTEGER_STRING_INDEX, + factory->NewFromString("exponentInteger").GetTaggedValue()); + SetConstant(ConstantIndex::LONG_STRING_INDEX, factory->NewFromString("long").GetTaggedValue()); + SetConstant(ConstantIndex::SHORT_STRING_INDEX, factory->NewFromString("short").GetTaggedValue()); + SetConstant(ConstantIndex::FULL_STRING_INDEX, factory->NewFromString("full").GetTaggedValue()); + SetConstant(ConstantIndex::MEDIUM_STRING_INDEX, factory->NewFromString("medium").GetTaggedValue()); + SetConstant(ConstantIndex::NARROW_STRING_INDEX, factory->NewFromString("narrow").GetTaggedValue()); + SetConstant(ConstantIndex::ALWAYS_STRING_INDEX, factory->NewFromString("always").GetTaggedValue()); + SetConstant(ConstantIndex::AUTO_STRING_INDEX, factory->NewFromString("auto").GetTaggedValue()); + SetConstant(ConstantIndex::UNIT_DISPLAY_INDEX, factory->NewFromString("unitDisplay").GetTaggedValue()); + SetConstant(ConstantIndex::NOTATION_INDEX, factory->NewFromString("notation").GetTaggedValue()); + SetConstant(ConstantIndex::COMPACT_DISPALY_INDEX, factory->NewFromString("compactDisplay").GetTaggedValue()); + SetConstant(ConstantIndex::USER_GROUPING_INDEX, factory->NewFromString("useGrouping").GetTaggedValue()); + SetConstant(ConstantIndex::SIGN_DISPLAY_INDEX, factory->NewFromString("signDisplay").GetTaggedValue()); + SetConstant(ConstantIndex::CODE_INDEX, factory->NewFromString("code").GetTaggedValue()); + SetConstant(ConstantIndex::NARROW_SYMBOL_INDEX, factory->NewFromString("narrowSymbol").GetTaggedValue()); + SetConstant(ConstantIndex::STANDARD_INDEX, factory->NewFromString("standard").GetTaggedValue()); + SetConstant(ConstantIndex::ACCOUNTING_INDEX, factory->NewFromString("accounting").GetTaggedValue()); + SetConstant(ConstantIndex::SCIENTIFIC_INDEX, factory->NewFromString("scientific").GetTaggedValue()); + SetConstant(ConstantIndex::ENGINEERING_INDEX, factory->NewFromString("engineering").GetTaggedValue()); + SetConstant(ConstantIndex::COMPACT_STRING_INDEX, factory->NewFromString("compact").GetTaggedValue()); + SetConstant(ConstantIndex::NEVER_INDEX, factory->NewFromString("never").GetTaggedValue()); + SetConstant(ConstantIndex::EXPECT_ZERO_INDEX, factory->NewFromString("exceptZero").GetTaggedValue()); + SetConstant(ConstantIndex::MINIMUM_INTEGER_DIGITS_INDEX, + factory->NewFromString("minimumIntegerDigits").GetTaggedValue()); + SetConstant(ConstantIndex::MINIMUM_FRACTIONDIGITS_INDEX, + factory->NewFromString("minimumFractionDigits").GetTaggedValue()); + SetConstant(ConstantIndex::MAXIMUM_FRACTIONDIGITS_INDEX, + factory->NewFromString("maximumFractionDigits").GetTaggedValue()); + SetConstant(ConstantIndex::MINIMUM_SIGNIFICANTDIGITS_INDEX, + factory->NewFromString("minimumSignificantDigits").GetTaggedValue()); + SetConstant(ConstantIndex::MAXIMUM_SIGNIFICANTDIGITS_INDEX, + factory->NewFromString("maximumSignificantDigits").GetTaggedValue()); + SetConstant(ConstantIndex::INVALID_DATE_INDEX, factory->NewFromString("InvalidDate").GetTaggedValue()); + SetConstant(ConstantIndex::USAGE_INDEX, factory->NewFromString("usage").GetTaggedValue()); + SetConstant(ConstantIndex::COMPARE_INDEX, factory->NewFromString("compare").GetTaggedValue()); + SetConstant(ConstantIndex::SENSITIVITY_INDEX, factory->NewFromString("sensitivity").GetTaggedValue()); + SetConstant(ConstantIndex::IGNORE_PUNCTUATION_INDEX, factory->NewFromString("ignorePunctuation").GetTaggedValue()); + SetConstant(ConstantIndex::CARDINAL_INDEX, factory->NewFromString("cardinal").GetTaggedValue()); + SetConstant(ConstantIndex::ORDINAL_INDEX, factory->NewFromString("ordinal").GetTaggedValue()); + SetConstant(ConstantIndex::EXEC_INDEX, factory->NewFromString("exec").GetTaggedValue()); + SetConstant(ConstantIndex::LAST_INDEX_INDEX, factory->NewFromString("lastIndex").GetTaggedValue()); + + auto accessor = factory->NewInternalAccessor(reinterpret_cast(JSFunction::PrototypeSetter), + reinterpret_cast(JSFunction::PrototypeGetter)); + SetConstant(ConstantIndex::FUNCTION_PROTOTYPE_ACCESSOR, accessor.GetTaggedValue()); + accessor = factory->NewInternalAccessor(nullptr, reinterpret_cast(JSFunction::NameGetter)); + SetConstant(ConstantIndex::FUNCTION_NAME_ACCESSOR, accessor.GetTaggedValue()); + accessor = factory->NewInternalAccessor(nullptr, reinterpret_cast(JSFunction::LengthGetter)); + SetConstant(ConstantIndex::FUNCTION_LENGTH_ACCESSOR, accessor.GetTaggedValue()); + accessor = factory->NewInternalAccessor(reinterpret_cast(JSArray::LengthSetter), + reinterpret_cast(JSArray::LengthGetter)); + SetConstant(ConstantIndex::ARRAY_LENGTH_ACCESSOR, accessor.GetTaggedValue()); +} + +void GlobalEnvConstants::InitGlobalUndefined() +{ + SetConstant(ConstantIndex::UNDEFINED_INDEX, JSTaggedValue::Undefined()); +} +} // namespace panda::ecmascript diff --git a/ecmascript/global_env_constants.h b/ecmascript/global_env_constants.h new file mode 100644 index 0000000000000000000000000000000000000000..7ca719d4fc170471a1216d63f1dcbd96ff7a268f --- /dev/null +++ b/ecmascript/global_env_constants.h @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_ECMASCRIPT_ECMA_ROOTS_H +#define RUNTIME_ECMASCRIPT_ECMA_ROOTS_H + +#include + +#include "ecmascript/mem/heap_roots.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +// Forward Declaration +class ObjectFactory; +template +class JSHandle; +class JSHClass; +class JSThread; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_CONSTANT_CLASS(V) \ + /* GC Root */ \ + V(JSTaggedValue, HClassClass, HCLASS_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, StringClass, STRING_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ArrayClass, ARRAY_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, DictionaryClass, DICTIONARY_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSNativePointerClass, JS_NATIVE_POINTER_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, EnvClass, ENV_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, FreeObjectWithNoneFieldClass, FREE_OBJECT_WITH_NONE_FIELD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, FreeObjectWithOneFieldClass, FREE_OBJECT_WITH_ONE_FIELD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, FreeObjectWithTwoFieldClass, FREE_OBJECT_WITH_TWO_FIELD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, SymbolClass, SYMBOL_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, AccessorDataClass, ACCESSOE_DATA_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, InternalAccessorClass, INTERNAL_ACCESSOR_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSProxyOrdinaryClass, JS_PROXY_ORDINARY_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ObjectWrapperClass, OBJECT_WRAPPER_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, CompletionRecordClass, COMPLETION_RECORD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, GeneratorContextClass, GENERATOR_CONTEST_INDEX, ecma_roots_class) \ + V(JSTaggedValue, CapabilityRecordClass, CAPABILITY_RECORD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ReactionsRecordClass, REACTIONS_RECORD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, PromiseIteratorRecordClass, PROMISE_ITERATOR_RECORD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, PromiseRecordClass, PROMISE_RECORD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, PromiseResolvingFunctionsRecordClass, PROMISE_RESOLVING_FUNCTIONS_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, MicroJobQueueClass, MICRO_JOB_QUEUE_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, PendingJobClass, PENDING_JOB_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ProtoChangeMarkerClass, PROTO_CHANGE_MARKER_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ProtoChangeDetailsClass, PROTO_CHANGE_DETAILS_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, PrototypeHandlerClass, PROTOTYPE_HANDLER_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, TransitionHandlerClass, TRANSITION_HANDLER_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, PropertyBoxClass, PROPERTY_BOX_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, FunctionExtraInfoClass, FUNCTION_EXTRA_INFO_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ProgramClass, PROGRAM_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, EcmaModuleClass, ECMA_MODULE_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSProxyCallableClass, JS_PROXY_CALLABLE_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSProxyConstructClass, JS_PROXY_CONSTRUCT_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSRealmClass, JS_REALM_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSRegExpClass, JS_REGEXP_CLASS_INDEX, ecma_roots_class) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_CONSTANT_SPECIAL(V) \ + V(JSTaggedValue, Undefined, UNDEFINED_INDEX, ecma_roots_special) \ + V(JSTaggedValue, Null, NULL_INDEX, ecma_roots_special) \ + V(JSTaggedValue, EmptyString, EMPTY_STRING_OBJECT_INDEX, ecma_roots_special) +/* GlobalConstant */ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_CONSTANT_CONSTANT(V) \ + V(JSTaggedValue, ConstructorString, CONSTRUCTOR_STRING_INDEX, constructor) \ + V(JSTaggedValue, PrototypeString, PROTOTYPE_STRING_INDEX, prototype) \ + V(JSTaggedValue, LengthString, LENGTH_STRING_INDEX, length) \ + V(JSTaggedValue, ValueString, VALUE_STRING_INDEX, value) \ + V(JSTaggedValue, GetString, GET_STRING_INDEX, set) \ + V(JSTaggedValue, SetString, SET_STRING_INDEX, get) \ + V(JSTaggedValue, WritableString, WRITABLE_STRING_INDEX, writable) \ + V(JSTaggedValue, EnumerableString, ENUMERABLE_STRING_INDEX, enumerable) \ + V(JSTaggedValue, ConfigurableString, CONFIGURABLE_STRING_INDEX, configurable) \ + /* SymbolTable*RegisterSymbols */ \ + V(JSTaggedValue, NameString, NAME_STRING_INDEX, name) \ + V(JSTaggedValue, GetPrototypeOfString, GETPROTOTYPEOF_STRING_INDEX, getPrototypeOf) \ + V(JSTaggedValue, SetPrototypeOfString, SETPROTOTYPEOF_STRING_INDEX, setPrototypeOf) \ + V(JSTaggedValue, IsExtensibleString, ISEXTENSIBLE_STRING_INDEX, isExtensible) \ + V(JSTaggedValue, PreventExtensionsString, PREVENTEXTENSIONS_STRING_INDEX, preventExtensions) \ + V(JSTaggedValue, GetOwnPropertyDescriptorString, GETOWNPROPERTYDESCRIPTOR_STRING_INDEX, getOwnPropertyDescriptor) \ + V(JSTaggedValue, DefinePropertyString, DEFINEPROPERTY_STRING_INDEX, defineProperty) \ + V(JSTaggedValue, HasString, HAS_STRING_INDEX, has) \ + V(JSTaggedValue, DeletePropertyString, DELETEPROPERTY_STRING_INDEX, deleteProperty) \ + V(JSTaggedValue, EnumerateString, ENUMERATE_STRING_INDEX, enumerate) \ + V(JSTaggedValue, OwnKeysString, OWNKEYS_STRING_INDEX, ownKeys) \ + V(JSTaggedValue, ApplyString, APPLY_STRING_INDEX, apply) \ + V(JSTaggedValue, NegativeZeroString, NEGATIVE_ZERO_STRING_INDEX, -0) \ + V(JSTaggedValue, DoneString, DONE_STRING_INDEX, done) \ + V(JSTaggedValue, ProxyString, PROXY_STRING_INDEX, proxy) \ + V(JSTaggedValue, RevokeString, REVOKE_STRING_INDEX, revoke) \ + V(JSTaggedValue, NextString, NEXT_STRING_INDEX, next) \ + V(JSTaggedValue, ToStringString, TO_STRING_STRING_INDEX, toString) \ + V(JSTaggedValue, ToLocaleStringString, TO_LOCALE_STRING_STRING_INDEX, toLocaleString) \ + V(JSTaggedValue, ValueOfString, VALUE_OF_STRING_INDEX, valueOf) \ + V(JSTaggedValue, UndefinedString, UNDEFINED_STRING_INDEX, undefined) \ + V(JSTaggedValue, NullString, NULL_STRING_INDEX, null) \ + V(JSTaggedValue, BooleanString, BOOLEAN_STRING_INDEX, boolean) \ + V(JSTaggedValue, NumberString, NUMBER_STRING_INDEX, number) \ + V(JSTaggedValue, FunctionString, FUNCTION_STRING_INDEX, function) \ + V(JSTaggedValue, StringString, STRING_STRING_INDEX, string) \ + V(JSTaggedValue, SymbolString, SYMBOL_STRING_INDEX, symbol) \ + V(JSTaggedValue, ObjectString, OBJECT_STRING_INDEX, object) \ + V(JSTaggedValue, TrueString, TRUE_STRING_INDEX, true) \ + V(JSTaggedValue, FalseString, FALSE_STRING_INDEX, false) \ + V(JSTaggedValue, ReturnString, RETURN_STRING_INDEX, return ) \ + V(JSTaggedValue, ProxyConstructString, PROXY_CONSTRUCT_STRING_INDEX, construct) \ + V(JSTaggedValue, ProxyCallString, PROXY_CALL_STRING_INDEX, call) \ + V(JSTaggedValue, PromiseThenString, PROMISE_THEN_STRING_INDEX, then) \ + V(JSTaggedValue, PromiseCatchString, PROMISE_CATCH_STRING_INDEX, catch) \ + V(JSTaggedValue, ScriptJobString, SCRIPT_JOB_STRING_INDEX, ScriptJobs) \ + V(JSTaggedValue, PromiseString, PROMISE_STRING_INDEX, PrimiseJobs) \ + V(JSTaggedValue, ThrowerString, THROWER_STRING_INDEX, Thrower) \ + V(JSTaggedValue, IdentityString, IDENTITY_STRING_INDEX, Identity) \ + V(JSTaggedValue, CallerString, CALLER_STRING_INDEX, caller) \ + V(JSTaggedValue, CalleeString, CALLEE_STRING_INDEX, callee) \ + V(JSTaggedValue, Int8ArrayString, INT8_ARRAY_STRING_INDEX, Int8Array) \ + V(JSTaggedValue, Uint8ArrayString, UINT8_ARRAY_STRING_INDEX, Uint8Array) \ + V(JSTaggedValue, Uint8ClampedArrayString, UINT8_CLAMPED_ARRAY_STRING_INDEX, Uint8ClampedArray) \ + V(JSTaggedValue, Int16ArrayString, INT16_ARRAY_STRING_INDEX, Int16Array) \ + V(JSTaggedValue, Uint16ArrayString, UINT16_ARRAY_STRING_INDEX, Uint16Array) \ + V(JSTaggedValue, Int32ArrayString, INT32_ARRAY_STRING_INDEX, Int32Array) \ + V(JSTaggedValue, Uint32ArrayString, UINT32_ARRAY_STRING_INDEX, Uint32Array) \ + V(JSTaggedValue, Float32ArrayString, FLOAT32_ARRAY_STRING_INDEX, Float32Array) \ + V(JSTaggedValue, Float64ArrayString, FLOAT64_ARRAY_STRING_INDEX, Float64Array) \ + V(JSTaggedValue, AsyncFunctionString, ASYNC_FUNCTION_STRING_INDEX, AsyncFunction) \ + V(JSTaggedValue, PromiseResolveString, PROMISE_RESOLVE_STRING_INDEX, resolve) \ + V(JSTaggedValue, IdString, ID_STRING_INDEX, id) \ + V(JSTaggedValue, MethodString, METHOD_STRING_INDEX, method) \ + V(JSTaggedValue, ParamsString, PARAMS_STRING_INDEX, params) \ + V(JSTaggedValue, ResultString, RESULT_STRING_INDEX, result) \ + V(JSTaggedValue, ToJsonString, TO_JSON_STRING_INDEX, toJSON) \ + V(JSTaggedValue, GlobalString, GLOBAL_STRING_INDEX, global) \ + V(JSTaggedValue, MessageString, MESSAGE_STRING_INDEX, message) \ + V(JSTaggedValue, ErrorString, ERROR_STRING_INDEX, Error) \ + V(JSTaggedValue, RangeErrorString, RANGE_ERROR_STRING_INDEX, RangeError) \ + V(JSTaggedValue, ReferenceErrorString, REFERENCE_ERROR_STRING_INDEX, ReferenceError) \ + V(JSTaggedValue, TypeErrorString, TYPE_ERROR_STRING_INDEX, TypeError) \ + V(JSTaggedValue, URIErrorString, URI_ERROR_STRING_INDEX, URIError) \ + V(JSTaggedValue, SyntaxErrorString, SYNTAX_ERROR_STRING_INDEX, SyntaxError) \ + V(JSTaggedValue, EvalErrorString, EVAL_ERROR_STRING_INDEX, EvalError) \ + V(JSTaggedValue, StackString, STACK_STRING_INDEX, stack) \ + V(JSTaggedValue, StackEmptyString, STACK_EMPTY_STRING_INDEX, stackisempty) \ + V(JSTaggedValue, ObjNotCoercibleString, OBJ_NOT_COERCIBLE_STRING_INDEX, objectnotcoercible) \ + /* forIntl. */ \ + V(JSTaggedValue, LanguageString, LANGUAGE_STRING_CLASS_INDEX, language) \ + V(JSTaggedValue, ScriptString, SCRIPT_STRING_CLASS_INDEX, script) \ + V(JSTaggedValue, RegionString, REGION_STRING_CLASS_INDEX, region) \ + V(JSTaggedValue, BaseNameString, BASE_NAME_STRING_CLASS_INDEX, baseName) \ + V(JSTaggedValue, CalendarString, CALENDAR_STRING_CLASS_INDEX, calendar) \ + V(JSTaggedValue, CollationString, COLLATION_STRING_CLASS_INDEX, collation) \ + V(JSTaggedValue, HourCycleString, HOUR_CYCLE_STRING_CLASS_INDEX, hourCycle) \ + V(JSTaggedValue, CaseFirstString, CASE_FIRST_STRING_CLASS_INDEX, caseFirst) \ + V(JSTaggedValue, NumericString, NUMERIC_STRING_CLASS_INDEX, numeric) \ + V(JSTaggedValue, NumberingSystemString, NUMBERING_SYSTEM_STRING_CLASS_INDEX, numberingSystem) \ + V(JSTaggedValue, TypeString, TYPE_STRING_INDEX, type) \ + V(JSTaggedValue, LocaleMatcherString, LOCALE_MATCHER_STRING_INDEX, localeMatcher) \ + V(JSTaggedValue, FormatMatcherString, FORMAT_MATCHER_STRING_INDEX, formatMatcher) \ + V(JSTaggedValue, Hour12String, HOUR12_STRING_INDEX, hour12) \ + V(JSTaggedValue, H11String, H11_STRING_INDEX, h11) \ + V(JSTaggedValue, H12String, H12_STRING_INDEX, h12) \ + V(JSTaggedValue, H23String, H23_STRING_INDEX, h23) \ + V(JSTaggedValue, H24String, H24_STRING_INDEX, h24) \ + V(JSTaggedValue, WeekdayString, WEEK_DAY_STRING_INDEX, weekday) \ + V(JSTaggedValue, EraString, ERA_STRING_INDEX, era) \ + V(JSTaggedValue, YearString, YEAR_STRING_INDEX, year) \ + V(JSTaggedValue, QuarterString, QUARTER_STRING_INDEX, quarter) \ + V(JSTaggedValue, MonthString, MONTH_STRING_INDEX, month) \ + V(JSTaggedValue, DayString, DAY_STRING_INDEX, day) \ + V(JSTaggedValue, HourString, HOUR_STRING_INDEX, hour) \ + V(JSTaggedValue, MinuteString, MINUTE_STRING_INDEX, minute) \ + V(JSTaggedValue, SecondString, SECOND_STRING_INDEX, second) \ + V(JSTaggedValue, YearsString, YEARS_STRING_INDEX, years) \ + V(JSTaggedValue, QuartersString, QUARTERS_STRING_INDEX, quarters) \ + V(JSTaggedValue, MonthsString, MONTHS_STRING_INDEX, months) \ + V(JSTaggedValue, DaysString, DAYS_STRING_INDEX, days) \ + V(JSTaggedValue, HoursString, HOURS_STRING_INDEX, hours) \ + V(JSTaggedValue, MinutesString, MINUTES_STRING_INDEX, minutes) \ + V(JSTaggedValue, SecondsString, SECONDS_STRING_INDEX, seconds) \ + V(JSTaggedValue, TimeZoneNameString, TIME_ZONE_NAME_STRING_INDEX, timeZoneName) \ + V(JSTaggedValue, LocaleString, LOCALE_STRING_INDEX, locale) \ + V(JSTaggedValue, TimeZoneString, TIME_ZONE_STRING_INDEX, timeZone) \ + V(JSTaggedValue, LiteralString, LITERAL_STRING_INDEX, literal) \ + V(JSTaggedValue, YearNameString, YEAR_NAME_STRING_INDEX, yearName) \ + V(JSTaggedValue, DayPeriodString, DAY_PERIOD_STRING_INDEX, dayPeriod) \ + V(JSTaggedValue, FractionalSecondDigitsString, FRACTIONAL_SECOND_DIGITS_STRING_INDEX, fractionalSecondDigits) \ + V(JSTaggedValue, FractionalSecondString, FRACTIONAL_SECOND_STRING_INDEX, fractionalSecond) \ + V(JSTaggedValue, RelatedYearString, RELATED_YEAR_STRING_INDEX, relatedYear) \ + V(JSTaggedValue, LookUpString, LOOK_UP_STRING_INDEX, lookup) \ + V(JSTaggedValue, BestFitString, BEST_FIT_STRING_INDEX, bestfit) \ + V(JSTaggedValue, DateStyleString, DATE_STYLE_STRING_INDEX, dateStyle) \ + V(JSTaggedValue, TimeStyleString, TIME_STYLE_STRING_INDEX, timeStyle) \ + V(JSTaggedValue, UTCString, UTC_STRING_INDEX, UTC) \ + V(JSTaggedValue, InitializedRelativeTimeFormatString, INITIALIZED_RELATIVE_INDEX, true) \ + V(JSTaggedValue, WeekString, WEEK_STRING_INDEX, week) \ + V(JSTaggedValue, WeeksString, WEEKS_STRING_INDEX, weeks) \ + V(JSTaggedValue, SourceString, SOURCE_STRING_INDEX, source) \ + V(JSTaggedValue, FormatString, FORMAT_STRING_INDEX, format) \ + V(JSTaggedValue, EnUsString, EN_US_STRING_INDEX, en - US) \ + V(JSTaggedValue, UndString, UND_STRING_INDEX, und) \ + V(JSTaggedValue, LatnString, LATN_STRING_INDEX, latn) \ + V(JSTaggedValue, StyleString, STYLE_STRING_INDEX, style) \ + V(JSTaggedValue, UnitString, UNIT_STRING_INDEX, unit) \ + V(JSTaggedValue, IntegerString, INTEGER_STRING_INDEX, integer) \ + V(JSTaggedValue, NanString, NAN_STRING_INDEX, nan) \ + V(JSTaggedValue, InfinityString, INFINITY_STRING_INDEX, infinity) \ + V(JSTaggedValue, FractionString, FRACTION_STRING_INDEX, fraction) \ + V(JSTaggedValue, DecimalString, DECIMAL_STRING_INDEX, decimal) \ + V(JSTaggedValue, GroupString, GROUP_STRING_INDEX, group) \ + V(JSTaggedValue, CurrencyString, CURRENCY_STRING_INDEX, currency) \ + V(JSTaggedValue, CurrencySignString, CURRENCY_SIGN_STRING_INDEX, currencySign) \ + V(JSTaggedValue, CurrencyDisplayString, CURRENCY_DISPLAY_STRING_INDEX, currencyDisplay) \ + V(JSTaggedValue, PercentSignString, PERCENT_SIGN_STRING_INDEX, percentSign) \ + V(JSTaggedValue, PercentString, PERCENT_STRING_INDEX, percent) \ + V(JSTaggedValue, MinusSignString, MINUS_SIGN_STRING_INDEX, minusSign) \ + V(JSTaggedValue, PlusSignString, PLUS_SIGN_STRING_INDEX, plusSign) \ + V(JSTaggedValue, ExponentSeparatorString, EXPONENT_SEPARATOR_STRING_INDEX, exponentSeparator) \ + V(JSTaggedValue, ExponentMinusSignString, EXPONENT_MINUS_SIGN_INDEX, exponentMinusSign) \ + V(JSTaggedValue, ExponentIntegerString, EXPONENT_INTEGER_STRING_INDEX, exponentInteger) \ + V(JSTaggedValue, LongString, LONG_STRING_INDEX, long) \ + V(JSTaggedValue, ShortString, SHORT_STRING_INDEX, short) \ + V(JSTaggedValue, FullString, FULL_STRING_INDEX, full) \ + V(JSTaggedValue, MediumString, MEDIUM_STRING_INDEX, medium) \ + V(JSTaggedValue, NarrowString, NARROW_STRING_INDEX, narrow) \ + V(JSTaggedValue, AlwaysString, ALWAYS_STRING_INDEX, always) \ + V(JSTaggedValue, AutoString, AUTO_STRING_INDEX, auto) \ + V(JSTaggedValue, UnitDisplayString, UNIT_DISPLAY_INDEX, unitDisplay) \ + V(JSTaggedValue, NotationString, NOTATION_INDEX, notation) \ + V(JSTaggedValue, CompactDisplayString, COMPACT_DISPALY_INDEX, compactDisplay) \ + V(JSTaggedValue, UserGroupingString, USER_GROUPING_INDEX, useGrouping) \ + V(JSTaggedValue, SignDisplayString, SIGN_DISPLAY_INDEX, signDisplay) \ + V(JSTaggedValue, CodeString, CODE_INDEX, code) \ + V(JSTaggedValue, NarrowSymbolString, NARROW_SYMBOL_INDEX, narrowSymbol) \ + V(JSTaggedValue, StandardString, STANDARD_INDEX, standard) \ + V(JSTaggedValue, AccountingString, ACCOUNTING_INDEX, accounting) \ + V(JSTaggedValue, ScientificString, SCIENTIFIC_INDEX, scientific) \ + V(JSTaggedValue, EngineeringString, ENGINEERING_INDEX, engineering) \ + V(JSTaggedValue, CompactString, COMPACT_STRING_INDEX, compact) \ + V(JSTaggedValue, NeverString, NEVER_INDEX, never) \ + V(JSTaggedValue, ExceptZeroString, EXPECT_ZERO_INDEX, exceptZero) \ + V(JSTaggedValue, MinimumIntegerDigitsString, MINIMUM_INTEGER_DIGITS_INDEX, minimumIntegerDigits) \ + V(JSTaggedValue, MinimumFractionDigitsString, MINIMUM_FRACTIONDIGITS_INDEX, minimumFractionDigits) \ + V(JSTaggedValue, MaximumFractionDigitsString, MAXIMUM_FRACTIONDIGITS_INDEX, maximumFractionDigits) \ + V(JSTaggedValue, MinimumSignificantDigitsString, MINIMUM_SIGNIFICANTDIGITS_INDEX, minimumSignificantDigits) \ + V(JSTaggedValue, MaximumSignificantDigitsString, MAXIMUM_SIGNIFICANTDIGITS_INDEX, maximumSignificantDigits) \ + V(JSTaggedValue, InvalidDateString, INVALID_DATE_INDEX, InvalidDate) \ + V(JSTaggedValue, UsageString, USAGE_INDEX, usage) \ + V(JSTaggedValue, CompareString, COMPARE_INDEX, compare) \ + V(JSTaggedValue, SensitivityString, SENSITIVITY_INDEX, sensitivity) \ + V(JSTaggedValue, IgnorePunctuationString, IGNORE_PUNCTUATION_INDEX, ignorePunctuation) \ + V(JSTaggedValue, CardinalString, CARDINAL_INDEX, cardinal) \ + V(JSTaggedValue, OrdinalString, ORDINAL_INDEX, ordinal) \ + /* for regexp. */ \ + V(JSTaggedValue, ExecString, EXEC_INDEX, exec) \ + V(JSTaggedValue, LastIndexString, LAST_INDEX_INDEX, lastIndex) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_CONSTANT_ACCESSOR(V) \ + V(JSTaggedValue, FunctionPrototypeAccessor, FUNCTION_PROTOTYPE_ACCESSOR, ecma_roots_accessor) \ + V(JSTaggedValue, FunctionNameAccessor, FUNCTION_NAME_ACCESSOR, ecma_roots_accessor) \ + V(JSTaggedValue, FunctionLengthAccessor, FUNCTION_LENGTH_ACCESSOR, ecma_roots_accessor) \ + V(JSTaggedValue, ArrayLengthAccessor, ARRAY_LENGTH_ACCESSOR, ecma_roots_accessor) +/* RealmConstant */ + +// ConstantIndex used for explicit visit each constant. +enum class ConstantIndex : uint16_t { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INDEX_FILTER(Type, Name, Index, Desc) Index, + GLOBAL_ENV_CONSTANT_CLASS(INDEX_FILTER) GLOBAL_ENV_CONSTANT_SPECIAL(INDEX_FILTER) + GLOBAL_ENV_CONSTANT_CONSTANT(INDEX_FILTER) GLOBAL_ENV_CONSTANT_ACCESSOR(INDEX_FILTER) + +#undef INDEX_FILTER + CONSTATNT_COUNT, + + CONSTATNT_BEGIN = 0, + CONSTATNT_END = CONSTATNT_COUNT, + + READ_ONLY_CONSTATNT_BEGIN = CONSTATNT_BEGIN, + READ_ONLY_CONSTATNT_END = CONSTATNT_END, + // ... +}; +// clang-format on + +class GlobalEnvConstants { +public: + explicit GlobalEnvConstants() = default; + + DEFAULT_MOVE_SEMANTIC(GlobalEnvConstants); + DEFAULT_COPY_SEMANTIC(GlobalEnvConstants); + + ~GlobalEnvConstants() = default; + + const JSTaggedValue *BeginSlot() const; + + const JSTaggedValue *EndSlot() const; + + void InitRootsClass(JSThread *thread, JSHClass *dynClassClass); + + void InitGlobalConstant(JSThread *thread); + + void InitGlobalUndefined(); + + void SetConstant(ConstantIndex index, JSTaggedValue value); + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECL_GET(Type, Name, Index, Desc) \ + const Type Get##Name() const; \ + const JSHandle GetHandled##Name() const; + GLOBAL_ENV_CONSTANT_CLASS(DECL_GET) + GLOBAL_ENV_CONSTANT_SPECIAL(DECL_GET) + GLOBAL_ENV_CONSTANT_CONSTANT(DECL_GET) + GLOBAL_ENV_CONSTANT_ACCESSOR(DECL_GET) +#undef DECL_GET + + void Visitor(const RootRangeVisitor &visitor) + { + visitor(ecmascript::Root::ROOT_VM, ObjectSlot(ToUintPtr(BeginSlot())), ObjectSlot(ToUintPtr(EndSlot()))); + } + +private: + JSTaggedValue constants_[static_cast(ConstantIndex::CONSTATNT_COUNT)]; // NOLINT(modernize-avoid-c-arrays) +}; +} // namespace panda::ecmascript +#endif // RUNTIME_ECMASCRIPT_ECMA_ROOTS_H diff --git a/ecmascript/global_handle_collection.h b/ecmascript/global_handle_collection.h new file mode 100644 index 0000000000000000000000000000000000000000..fd02adc16f1a6287572b1bd413004153c18e0192 --- /dev/null +++ b/ecmascript/global_handle_collection.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_GLOBAL_HANDLE_COLLECTION_H +#define PANDA_RUNTIME_ECMASCRIPT_GLOBAL_HANDLE_COLLECTION_H + +#include "ecmascript/js_thread.h" + +namespace panda::ecmascript { +template +class JSHandle; +class GlobalHandleCollection { +public: + explicit GlobalHandleCollection(JSThread *thread) : thread_(thread) {} + + ~GlobalHandleCollection() = default; + + DEFAULT_MOVE_SEMANTIC(GlobalHandleCollection); + DEFAULT_COPY_SEMANTIC(GlobalHandleCollection); + + template + JSHandle NewHandle(JSTaggedType value) + { + uintptr_t addr = thread_->GetGlobalHandleStorage()->NewGlobalHandle(value); + return JSHandle(addr); + } + + void Dispose(HandleBase handle) + { + thread_->GetGlobalHandleStorage()->DisposeGlobalHandle(handle.GetAddress()); + } + +private: + JSThread *thread_{nullptr}; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_GLOBAL_HANDLE_COLLECTION_H diff --git a/ecmascript/hprof/heap_profiler.cpp b/ecmascript/hprof/heap_profiler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..759c71a45edab5dc80d4d5d9309a16ee7f385721 --- /dev/null +++ b/ecmascript/hprof/heap_profiler.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/hprof/heap_profiler.h" + +#include +#include +#include + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/hprof/heap_snapshot.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/assert_scope-inl.h" +#include "ecmascript/mem/heap.h" + +namespace panda::ecmascript { +HeapProfiler::~HeapProfiler() +{ + ClearSnapShot(); + allocator_->Finalize(jsonSerializer_); + jsonSerializer_ = nullptr; +} + +bool HeapProfiler::DumpHeapSnapShot(JSThread *thread, DumpFormat dumpFormat, const CString &filePath) +{ + [[maybe_unused]] bool heapClean = ForceFullGC(thread); + ASSERT(heapClean); + HeapSnapShot *snapShot = MakeHeapSnapShot(thread, SampleType::ONE_SHOT); + ASSERT(snapShot != nullptr); + std::pair realPath = FilePathValid(filePath); + if (realPath.first) { + return jsonSerializer_->Serialize(snapShot, realPath.second); + } + + std::pair realGenPath = FilePathValid(GenDumpFileName(dumpFormat)); + if (realGenPath.first) { + return jsonSerializer_->Serialize(snapShot, realGenPath.second); + } + UNREACHABLE(); +} + +bool HeapProfiler::StartHeapTracking(JSThread *thread, double timeInterval) +{ + HeapSnapShot *snapShot = MakeHeapSnapShot(thread, SampleType::REAL_TIME); + if (snapShot == nullptr) { + return false; + } + heapTracker_ = std::make_unique(snapShot, timeInterval); + thread->GetEcmaVM()->StartHeapTracking(heapTracker_.get()); + heapTracker_->StartTracing(); + return true; +} + +bool HeapProfiler::StopHeapTracking(JSThread *thread, DumpFormat dumpFormat, const CString &path) +{ + if (heapTracker_ == nullptr) { + return false; + } + thread->GetEcmaVM()->StopHeapTracking(); + heapTracker_->StopTracing(); + + // check path + if (path.empty()) { + return false; + } + std::pair realPath = FilePathValid(path); + if (!realPath.first) { + return false; + } + + HeapSnapShot *snapShot = hprofs_.at(0); + if (snapShot == nullptr) { + return false; + } + snapShot->FinishSnapShot(); + return jsonSerializer_->Serialize(snapShot, realPath.second); +} + +std::pair HeapProfiler::FilePathValid(const CString &filePath) +{ + if (filePath.size() > PATH_MAX) { + return std::make_pair(false, ""); + } + CVector resolvedPath(PATH_MAX); + auto result = realpath(filePath.c_str(), resolvedPath.data()); + if (result == resolvedPath.data() || errno == ENOENT) { + return std::make_pair(true, CString(resolvedPath.data())); + } + return std::make_pair(false, ""); +} + +CString HeapProfiler::GenDumpFileName(DumpFormat dumpFormat) +{ + CString filename("hprof_"); + switch (dumpFormat) { + case DumpFormat::JSON: + filename.append(GetTimeStamp()); + break; + case DumpFormat::BINARY: + filename.append("unimplemented"); + break; + case DumpFormat::OTHER: + filename.append("unimplemented"); + break; + default: + filename.append("unimplemented"); + break; + } + filename.append(".heapsnapshot"); + return filename; +} + +CString HeapProfiler::GetTimeStamp() +{ + std::time_t timeSource = std::time(nullptr); + tm *timeData = localtime(&timeSource); + CString stamp; + const int TIME_START = 1900; + stamp.append(ToCString(timeData->tm_year + TIME_START)) + .append("-") + .append(ToCString(timeData->tm_mon + 1)) + .append("-") + .append(ToCString(timeData->tm_mday)) + .append("_") + .append(ToCString(timeData->tm_hour)) + .append("-") + .append(ToCString(timeData->tm_min)) + .append("-") + .append(ToCString(timeData->tm_sec)); + return stamp; +} + +bool HeapProfiler::ForceFullGC(JSThread *thread) +{ + ASSERT(thread != nullptr); + auto vm = thread->GetEcmaVM(); + if (vm->IsInitialized()) { + const_cast(vm->GetHeap())->CollectGarbage(TriggerGCType::SEMI_GC); + const_cast(vm->GetHeap())->CollectGarbage(TriggerGCType::OLD_GC); + const_cast(vm->GetHeap())->CollectGarbage(TriggerGCType::NON_MOVE_GC); + return true; + } + return false; +} + +HeapSnapShot *HeapProfiler::MakeHeapSnapShot(JSThread *thread, SampleType sampleType) +{ + DISALLOW_GARBAGE_COLLECTION; + switch (sampleType) { + case SampleType::ONE_SHOT: { + auto *snapShot = allocator_->New(thread, allocator_); + if (snapShot == nullptr) { + LOG_ECMA(FATAL) << "alloc snapshot failed"; + UNREACHABLE(); + } + snapShot->BuildUp(thread); + AddSnapShot(snapShot); + return snapShot; + } + case SampleType::REAL_TIME: { + auto *snapShot = allocator_->New(thread, allocator_); + if (snapShot == nullptr) { + LOG_ECMA(FATAL) << "alloc snapshot failed"; + UNREACHABLE(); + } + AddSnapShot(snapShot); + snapShot->PrepareSnapShot(); + return snapShot; + } + default: + return nullptr; + } +} + +void HeapProfiler::AddSnapShot(HeapSnapShot *snapshot) +{ + if (hprofs_.size() >= MAX_NUM_HPROF) { + ClearSnapShot(); + } + ASSERT(snapshot != nullptr); + hprofs_.emplace_back(snapshot); +} + +void HeapProfiler::ClearSnapShot() +{ + for (auto *snapshot : hprofs_) { + allocator_->Finalize(snapshot); + } + hprofs_.clear(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/hprof/heap_profiler.h b/ecmascript/hprof/heap_profiler.h new file mode 100644 index 0000000000000000000000000000000000000000..15ebb50e446ff52d6ab0906820d19f6dff43b9dc --- /dev/null +++ b/ecmascript/hprof/heap_profiler.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_ECMASCRIPT_HPROF_HEAP_PROFILER_H +#define RUNTIME_ECMASCRIPT_HPROF_HEAP_PROFILER_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/hprof/heap_profiler_interface.h" +#include "ecmascript/hprof/heap_snapshot_json_serializer.h" +#include "ecmascript/hprof/heap_tracker.h" +#include "ecmascript/mem/c_containers.h" +#include "os/mem.h" + +namespace panda::ecmascript { +class HeapSnapShot; +class HeapProfiler : public HeapProfilerInterface { +public: + NO_MOVE_SEMANTIC(HeapProfiler); + NO_COPY_SEMANTIC(HeapProfiler); + explicit HeapProfiler(CAddressAllocator *allocator) : allocator_(allocator) + { + ASSERT(allocator != nullptr); + jsonSerializer_ = allocator->New(); + if (UNLIKELY(jsonSerializer_ == nullptr)) { + LOG_ECMA(FATAL) << "alloc snapshot json serializer failed"; + UNREACHABLE(); + } + } + ~HeapProfiler() override; + + enum class SampleType { ONE_SHOT, REAL_TIME }; + /** + * dump the specific snapshot in target format + */ + bool DumpHeapSnapShot(JSThread *thread, DumpFormat dumpFormat, const CString &path); + void AddSnapShot(HeapSnapShot *snapshot); + + bool StartHeapTracking(JSThread *thread, double timeInterval) override; + bool StopHeapTracking(JSThread *thread, DumpFormat dumpFormat, const CString &filePath) override; + +private: + /** + * tigger full gc to make sure no unreachable objects in heap + */ + bool ForceFullGC(JSThread *thread); + + /** + * make a new heap snapshot and put it into a container eg, vector + */ + HeapSnapShot *MakeHeapSnapShot(JSThread *thread, SampleType sampleType); + std::pair FilePathValid(const CString &filePath); + CString GenDumpFileName(DumpFormat dumpFormat); + CString GetTimeStamp(); + void ClearSnapShot(); + + const size_t MAX_NUM_HPROF = 5; // ~10MB + CAddressAllocator *allocator_{nullptr}; + CVector hprofs_; + HeapSnapShotJSONSerializer *jsonSerializer_{nullptr}; + std::unique_ptr heapTracker_; +}; +} // namespace panda::ecmascript +#endif // RUNTIME_ECMASCRIPT_HPROF_HEAP_PROFILER_H diff --git a/ecmascript/hprof/heap_profiler_interface.cpp b/ecmascript/hprof/heap_profiler_interface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e2ba64055115576ee348ffcf9d75db7402e0314 --- /dev/null +++ b/ecmascript/hprof/heap_profiler_interface.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/hprof/heap_profiler.h" +#include "ecmascript/js_thread.h" + +namespace panda::ecmascript { +void HeapProfilerInterface::DumpHeapSnapShot(JSThread *thread, DumpFormat dumpFormat, const CString &filePath) +{ + auto allocator = thread->GetEcmaVM()->GetCountAllocator(); + auto *hprof = allocator->New(allocator); + if (UNLIKELY(hprof == nullptr)) { + LOG_ECMA(FATAL) << "alloc hprof failed"; + UNREACHABLE(); + } + hprof->DumpHeapSnapShot(thread, dumpFormat, filePath); + allocator->Finalize(hprof); +} + +HeapProfilerInterface *HeapProfilerInterface::CreateHeapProfiler(JSThread *thread) +{ + auto allocator = thread->GetEcmaVM()->GetCountAllocator(); + auto *hprof = allocator->New(allocator); + ASSERT(hprof != nullptr); + return hprof; +} + +void HeapProfilerInterface::Destory(JSThread *thread, HeapProfilerInterface *heapProfiler) +{ + auto allocator = thread->GetEcmaVM()->GetCountAllocator(); + allocator->Finalize(heapProfiler); +} +} // namespace panda::ecmascript diff --git a/ecmascript/hprof/heap_profiler_interface.h b/ecmascript/hprof/heap_profiler_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..e27142ff5d775e452ebd06b74534c6525af515a7 --- /dev/null +++ b/ecmascript/hprof/heap_profiler_interface.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_ECMASCRIPT_HPROF_HEAP_PROFILER_INTERFACE_H +#define RUNTIME_ECMASCRIPT_HPROF_HEAP_PROFILER_INTERFACE_H + +#include "ecmascript/mem/c_string.h" +#include "ecmascript/js_thread.h" + +namespace panda::ecmascript { +enum class DumpFormat { JSON, BINARY, OTHER }; + +class HeapProfilerInterface { +public: + static void DumpHeapSnapShot(JSThread *thread, DumpFormat dumpFormat, const CString &filePath); + + static HeapProfilerInterface *CreateHeapProfiler(JSThread *thread); + static void Destory(JSThread *thread, HeapProfilerInterface *heapProfiler); + + HeapProfilerInterface() = default; + virtual ~HeapProfilerInterface() = default; + + virtual bool StartHeapTracking(JSThread *thread, double timeInterval) = 0; + virtual bool StopHeapTracking(JSThread *thread, DumpFormat dumpFormat, const CString &filePath) = 0; + + NO_MOVE_SEMANTIC(HeapProfilerInterface); + NO_COPY_SEMANTIC(HeapProfilerInterface); +}; +} // namespace panda::ecmascript +#endif // RUNTIME_ECMASCRIPT_HPROF_HEAP_PROFILER_INTERFACE_H diff --git a/ecmascript/hprof/heap_root_visitor.cpp b/ecmascript/hprof/heap_root_visitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f2f8c67a88493670c0aafadd8563590a4521920c --- /dev/null +++ b/ecmascript/hprof/heap_root_visitor.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/hprof/heap_root_visitor.h" +#include "ecmascript/js_thread.h" + +namespace panda::ecmascript { +void HeapRootVisitor::VisitHeapRoots(JSThread *thread, const RootVisitor &visitor, + const RootRangeVisitor &range_visitor) +{ + auto ecma_vm = GetVMInstance(thread); + ecma_vm->Iterate(visitor); + thread->Iterate(visitor, range_visitor); +} + +EcmaVM *HeapRootVisitor::GetVMInstance(JSThread *thread) const +{ + return thread->GetEcmaVM(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/hprof/heap_root_visitor.h b/ecmascript/hprof/heap_root_visitor.h new file mode 100644 index 0000000000000000000000000000000000000000..0b317e4fcc288bab5092ebaf3a53f5b7e41948a9 --- /dev/null +++ b/ecmascript/hprof/heap_root_visitor.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_ECMASCRIPT_HPROF_HEAP_ROOT_VISITOR_H +#define RUNTIME_ECMASCRIPT_HPROF_HEAP_ROOT_VISITOR_H + +#include "ecmascript/mem/heap_roots.h" + +namespace panda::ecmascript { +class JSThread; + +class HeapRootVisitor { +public: + HeapRootVisitor() = default; + ~HeapRootVisitor() = default; + NO_MOVE_SEMANTIC(HeapRootVisitor); + NO_COPY_SEMANTIC(HeapRootVisitor); + void VisitHeapRoots(JSThread *thread, const RootVisitor &visitor, const RootRangeVisitor &range_visitor); + +private: + EcmaVM *GetVMInstance(JSThread *thread) const; +}; +} // namespace panda::ecmascript +#endif // RUNTIME_ECMASCRIPT_HPROF_HEAP_ROOT_VISITOR_H diff --git a/ecmascript/hprof/heap_snapshot.cpp b/ecmascript/hprof/heap_snapshot.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e6b373cefd1808c203ff02a5710f65d2c33daeea --- /dev/null +++ b/ecmascript/hprof/heap_snapshot.cpp @@ -0,0 +1,717 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/hprof/heap_snapshot.h" + +#include + +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/global_dictionary.h" +#include "ecmascript/global_env.h" +#include "ecmascript/hprof/heap_root_visitor.h" +#include "ecmascript/ic/property_box.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_symbol.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/assert_scope-inl.h" +#include "ecmascript/property_attributes.h" +#include "ecmascript/tagged_array.h" +#include "ecmascript/tagged_dictionary.h" + +namespace panda::ecmascript { +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +CAddressAllocator *HeapSnapShot::allocator_ = nullptr; + +CAddressAllocator *HeapSnapShot::GetAllocator() +{ + ASSERT(allocator_ != nullptr); + return allocator_; +} + +CString *HeapSnapShot::GetString(const CString &as) +{ + return stringTable_.GetString(as); +} + +Node *Node::NewNode(size_t id, size_t index, CString *name, NodeType type, size_t size, TaggedObject *entry, + bool isLive) +{ + auto node = HeapSnapShot::GetAllocator()->New(id, index, name, type, size, 0, NewAddress(entry), + isLive); + if (UNLIKELY(node == nullptr)) { + LOG_ECMA(FATAL) << "internal allocator failed"; + UNREACHABLE(); + } + return node; +} + +Edge *Edge::NewEdge(uint64_t id, EdgeType type, Node *from, Node *to, CString *name) +{ + auto edge = HeapSnapShot::GetAllocator()->New(id, type, from, to, name); + if (UNLIKELY(edge == nullptr)) { + LOG_ECMA(FATAL) << "internal allocator failed"; + UNREACHABLE(); + } + return edge; +} + +HeapSnapShot::~HeapSnapShot() +{ + for (Node *node : nodes_) { + HeapSnapShot::GetAllocator()->Finalize(node); + } + for (Edge *edge : edges_) { + HeapSnapShot::GetAllocator()->Finalize(edge); + } + nodes_.clear(); + edges_.clear(); +} + +bool HeapSnapShot::BuildUp(JSThread *thread) +{ + FillNodes(thread); + FillEdges(thread); + AddSyntheticRoot(thread); + return Verify(); +} + +bool HeapSnapShot::Verify() +{ + GetString(CString("HeapVerify:").append(ToCString(totalNodesSize_))); + return (edgeCount_ > nodeCount_) && (totalNodesSize_ > 0); +} + +void HeapSnapShot::PrepareSnapShot() +{ + FillNodes(thread_); +} + +void HeapSnapShot::UpdateNode() +{ + for (Node *node : nodes_) { + node->SetLive(false); + } + FillNodes(thread_); + + for (auto iter = nodes_.begin(); iter != nodes_.end();) { + if (!(*iter)->IsLive()) { + iter = nodes_.erase(iter); + } else { + iter++; + } + } +} + +bool HeapSnapShot::FinishSnapShot() +{ + UpdateNode(); + FillEdges(thread_); + AddSyntheticRoot(thread_); + return Verify(); +} + +void HeapSnapShot::RecordSampleTime() +{ + timeStamps_.emplace_back(sequenceId_); +} + +void HeapSnapShot::AddNode(uintptr_t address) +{ + GenerateNode(thread_, JSTaggedValue(address)); +} + +void HeapSnapShot::MoveNode(uintptr_t address, uintptr_t forward_address) +{ + int sequenceId = -1; + Node *node = entryMap_.FindAndEraseNode(address); + if (node != nullptr) { + sequenceId = node->GetId(); + EraseNodeUnique(node); + } + GenerateNode(thread_, JSTaggedValue(forward_address), sequenceId); +} + +// NOLINTNEXTLINE(readability-function-size) +CString *HeapSnapShot::GenerateNodeName(JSThread *thread, TaggedObject *entry) +{ + CString *name = GetString("UnKnownType"); + auto *hCls = entry->GetClass(); + if (hCls->IsTaggedArray()) { + TaggedArray *array = TaggedArray::Cast(entry); + CString arrayName("Array["); + arrayName.append(ToCString(array->GetLength())); + arrayName.append("]"); + name = GetString(arrayName); // String type was handled singly, see#GenerateStringNode + } else if (hCls->IsHClass()) { + name = GetString("HiddenClass"); + } else if (hCls->IsJSNativePointer()) { + name = GetString("JSNativePointer"); + } else { + if (hCls->IsRealm()) { + name = GetString("JSRealm"); + } else if (hCls->IsString()) { + name = GetString("JsString"); + } else if (hCls->IsJSSymbol()) { + name = GetString("JSSymbol"); + } else if (hCls->IsJSArray()) { + name = GetString("JSArray"); + } else if (hCls->IsTypedArray()) { + name = GetString("TypedArray"); + } else if (hCls->IsJSTypedArray()) { + name = GetString("JSTypedArray"); + } else if (hCls->IsJSInt8Array()) { + name = GetString("JSInt8Array"); + } else if (hCls->IsJSUint8Array()) { + name = GetString("JSUint8Array"); + } else if (hCls->IsJSUint8ClampedArray()) { + name = GetString("JSUint8ClampedArray"); + } else if (hCls->IsJSInt16Array()) { + name = GetString("JSInt16Array"); + } else if (hCls->IsJSUint16Array()) { + name = GetString("JSUint16Array"); + } else if (hCls->IsJSInt32Array()) { + name = GetString("JSInt32Array"); + } else if (hCls->IsJSUint32Array()) { + name = GetString("JSUint32Array"); + } else if (hCls->IsJSFloat32Array()) { + name = GetString("JSFloat32Array"); + } else if (hCls->IsJSFloat64Array()) { + name = GetString("JSFloat64Array"); + } else if (hCls->IsJsGlobalEnv()) { + name = GetString("JSGlobalEnv"); + } else if (hCls->IsJSFunctionBase()) { + name = GetString("JSFunctionBase"); + } else if (hCls->IsJsBoundFunction()) { + name = GetString("JsBoundFunction"); + } else if (hCls->IsJSProxyRevocFunction()) { + name = GetString("JSProxyRevocFunction"); + } else if (hCls->IsJSAsyncFunction()) { + name = GetString("JSAsyncFunction"); + } else if (hCls->IsJSAsyncAwaitStatusFunction()) { + name = GetString("JSAsyncAwaitStatusFunction"); + } else if (hCls->IsJSPromiseReactionFunction()) { + name = GetString("JSPromiseReactionFunction"); + } else if (hCls->IsJSPromiseExecutorFunction()) { + name = GetString("JSPromiseExecutorFuncton"); + } else if (hCls->IsJSPromiseAllResolveElementFunction()) { + name = GetString("JSPromiseAllResolveElementFunction"); + } else if (hCls->IsJSFunctionExtraInfo()) { + name = GetString("JSFunctionExtraInfo"); + } else if (hCls->IsMicroJobQueue()) { + name = GetString("MicroJobQueue"); + } else if (hCls->IsPendingJob()) { + name = GetString("PendingJob"); + } else if (hCls->IsJsPrimitiveRef()) { + name = GetString("JsPrimitiveRef"); + } else if (hCls->IsJSSet()) { + name = GetString("JSSet"); + } else if (hCls->IsJSMap()) { + name = GetString("JSMap"); + } else if (hCls->IsJSWeakMap()) { + name = GetString("JSWeakMap"); + } else if (hCls->IsJSWeakSet()) { + name = GetString("JSWeakSet"); + } else if (hCls->IsJSFunction()) { + name = GetString("JSFunction"); + } else if (hCls->IsJSError()) { + name = GetString("JSError"); + } else if (hCls->IsArguments()) { + name = GetString("Arguments"); + } else if (hCls->IsDate()) { + name = GetString("Date"); + } else if (hCls->IsJSRegExp()) { + name = GetString("JSRegExp"); + } else if (hCls->IsJSProxy()) { + name = GetString("JSProxy"); + } else if (hCls->IsAccessorData()) { + name = GetString("AccessorData"); + } else if (hCls->IsInternalAccessor()) { + name = GetString("InternalAccessor"); + } else if (hCls->IsIterator()) { + name = GetString("Iterator"); + } else if (hCls->IsForinIterator()) { + name = GetString("ForinIterator"); + } else if (hCls->IsStringIterator()) { + name = GetString("StringIterator"); + } else if (hCls->IsArrayBuffer()) { + name = GetString("ArrayBuffer"); + } else if (hCls->IsDataView()) { + name = GetString("DataView"); + } else if (hCls->IsJSSetIterator()) { + name = GetString("JSSetIterator"); + } else if (hCls->IsJSMapIterator()) { + name = GetString("JSMapIterator"); + } else if (hCls->IsJSArrayIterator()) { + name = GetString("JSArrayIterator"); + } else if (hCls->IsPrototypeHandler()) { + name = GetString("PrototypeHandler"); + } else if (hCls->IsTransitionHandler()) { + name = GetString("TransitionHandler"); + } else if (hCls->IsPropertyBox()) { + name = GetString("PropertyBox"); + } else if (hCls->IsProtoChangeMarker()) { + name = GetString("ProtoChangeMarker"); + } else if (hCls->IsProtoChangeDetails()) { + name = GetString("ProtoChangeDetails"); + } else if (hCls->IsProgram()) { + name = GetString("Program"); + } else if (hCls->IsEcmaModule()) { + name = GetString("EcmaModule"); + } else if (hCls->IsLexicalFunction()) { + name = GetString("LexicalFunction"); + } else if (hCls->IsConstructor()) { + name = GetString("Constructor"); + } else if (hCls->IsExtensible()) { + name = GetString("Extensible"); + } else if (hCls->IsPrototype()) { + name = GetString("Prototype"); + } else if (hCls->IsLiteral()) { + name = GetString("Literal"); + } else if (hCls->IsClassConstructor()) { + name = GetString("ClassConstructor"); + } else if (hCls->IsJSGlobalObject()) { + name = GetString("JSGlobalObject"); + } else if (hCls->IsClassPrototype()) { + name = GetString("ClassPrototype"); + } else if (hCls->IsObjectWrapper()) { + name = GetString("ObjectWrapper"); + } else if (hCls->IsGeneratorFunction()) { + name = GetString("GeneratorFunction"); + } else if (hCls->IsGeneratorObject()) { + name = GetString("GeneratorObject"); + } else if (hCls->IsAsyncFuncObject()) { + name = GetString("AsyncFunction"); + } else if (hCls->IsJSPromise()) { + name = GetString("JSPromise"); + } else if (hCls->IsResolvingFunctionsRecord()) { + name = GetString("ResolvingFunctionsRecord"); + } else if (hCls->IsPromiseRecord()) { + name = GetString("PromiseRecord"); + } else if (hCls->IsPromiseIteratorRecord()) { + name = GetString("JSPromiseIteratorRecord"); + } else if (hCls->IsPromiseCapability()) { + name = GetString("PromiseCapability"); + } else if (hCls->IsPromiseReaction()) { + name = GetString("JSPromiseReaction"); + } else if (hCls->IsCompletionRecord()) { + name = GetString("CompletionRecord"); + } else if (hCls->IsRecord()) { + name = GetString("Record"); + } else if (hCls->IsTemplateMap()) { + name = GetString("TemplateMap"); + } else if (hCls->IsFreeObjectWithOneField()) { + name = GetString("FreeObjectWithOneField"); + } else if (hCls->IsFreeObjectWithTwoField()) { + name = GetString("FreeObjectWithTwoField"); + } else if (hCls->IsJSObject()) { + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + CString objName = CString("JSOBJECT(Ctor="); // Ctor-name + JSTaggedValue proto = JSObject::Cast(entry)->GetPrototype(thread); + JSHandle protoHandle(thread, proto); + if (protoHandle->IsNull() || protoHandle->IsUndefined()) { + name = GetString("JSObject(Ctor=UnKnown)"); + return name; + } + JSHandle ctor = + JSObject::GetProperty(thread, protoHandle, globalConst->GetHandledConstructorString()).GetValue(); + if (ctor->IsJSFunction()) { + JSHandle nameKey = globalConst->GetHandledNameString(); + JSHandle value = JSObject::GetProperty(thread, ctor, nameKey).GetValue(); + CString ctorName = EntryVisitor::ConvertKey(value.GetTaggedValue()); + objName.append(ctorName).append(")"); + name = GetString(objName); + } + } else if (hCls->IsECMAObject()) { + name = GetString("ECMAObject"); + } else { + name = GetString("UnEmuratedJSType"); + } + return name; // Cached in String-Table + } + return name; +} + +NodeType HeapSnapShot::GenerateNodeType(TaggedObject *entry) +{ + NodeType nodeType; + auto *hCls = entry->GetClass(); + if (hCls->IsTaggedArray()) { + nodeType = NodeType::JS_ARRAY; + } else if (hCls->IsHClass()) { + nodeType = NodeType::PROPERTY_BOX; + } else { + nodeType = NodeType(hCls->GetObjectType()); + } + return nodeType; +} + +void HeapSnapShot::FillNodes(JSThread *thread) +{ + // Iterate Heap Object + auto heap = thread_->GetEcmaVM()->GetHeap(); + if (heap != nullptr) { + heap->IteratorOverObjects( + [this, &thread](TaggedObject *obj) { this->GenerateNode(thread, JSTaggedValue(obj)); }); + } +} + +Node *HeapSnapShot::GenerateNode(JSThread *thread, JSTaggedValue entry, int sequenceId) +{ + Node *node = nullptr; + if (sequenceId == -1) { + sequenceId = sequenceId_ + SEQ_STEP; + } + if (entry.IsHeapObject()) { + if (entry.IsString()) { + return GenerateStringNode(entry, sequenceId); + } + TaggedObject *obj = entry.GetTaggedObject(); + auto *baseClass = obj->GetClass(); + if (baseClass != nullptr) { + node = Node::NewNode(sequenceId, nodeCount_, GenerateNodeName(thread, obj), GenerateNodeType(obj), + obj->GetObjectSize(), obj); + Node *existNode = entryMap_.FindOrInsertNode(node); // Fast Index + if (existNode == node) { + if (sequenceId == sequenceId_ + SEQ_STEP) { + sequenceId_ = sequenceId; // Odd Digit + } + InsertNodeUnique(node); + ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress()); + } else { + existNode->SetLive(true); + ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress()); + HeapSnapShot::GetAllocator()->Finalize(node); + return nullptr; + } + } + } else { + auto *obj = reinterpret_cast(entry.GetRawData()); + CString primitiveName; + JSTaggedValue primitiveObj(obj); + if (primitiveObj.IsInt()) { + primitiveName.append("Int:"); + } else if (primitiveObj.IsDouble()) { + primitiveName.append("Double:"); + } else if (primitiveObj.IsSpecial()) { + if (primitiveObj.IsHole()) { + primitiveName.append("Hole"); + } else if (primitiveObj.IsNull()) { + primitiveName.append("Null"); + } else if (primitiveObj.IsTrue()) { + primitiveName.append("Boolean:true"); + } else if (primitiveObj.IsFalse()) { + primitiveName.append("Boolean:false"); + } else if (primitiveObj.IsException()) { + primitiveName.append("Exception"); + } else if (primitiveObj.IsUndefined()) { + primitiveName.append("Undefined"); + } + } else { + primitiveName.append("Illegal_Primitive"); + } + + node = Node::NewNode(sequenceId, nodeCount_, GetString(primitiveName), NodeType::JS_PRIMITIVE_REF, 0, obj); + Node *existNode = entryMap_.FindOrInsertNode(node); // Fast Index + if (existNode == node) { + if (sequenceId == sequenceId_ + SEQ_STEP) { + sequenceId_ = sequenceId; // Odd Digit + } + InsertNodeUnique(node); + } else { + allocator_->Finalize(node); + node = nullptr; + existNode->SetLive(true); + } + } + return node; +} + +Node *HeapSnapShot::GenerateStringNode(JSTaggedValue entry, int sequenceId) +{ + Node *node = nullptr; + auto originStr = static_cast(entry.GetTaggedObject()); + size_t selfsize = originStr->ObjectSize(); + CString strContent; + strContent.append(EntryVisitor::ConvertKey(entry)); + node = Node::NewNode(sequenceId, nodeCount_, GetString(strContent), NodeType::PRIM_STRING, selfsize, + entry.GetTaggedObject()); + Node *existNode = entryMap_.FindOrInsertNode(node); // Fast Index + if (existNode == node) { + if (sequenceId == sequenceId_ + SEQ_STEP) { + sequenceId_ = sequenceId; // Odd Digit + } + InsertNodeUnique(node); + } else { + existNode->SetLive(true); + } + ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress()); + if (existNode != node) { + HeapSnapShot::GetAllocator()->Finalize(node); + return nullptr; + } + return node; +} + +void HeapSnapShot::FillEdges(JSThread *thread) +{ + size_t length = nodes_.size(); + auto iter = nodes_.begin(); + size_t count = 0; + while (++count < length) { + ASSERT(*iter != nullptr); + auto *objFrom = reinterpret_cast((*iter)->GetAddress()); + std::vector> nameResources; + JSTaggedValue(objFrom).DumpForSnapshot(thread, nameResources); + for (auto const &it : nameResources) { + auto *to = reinterpret_cast(it.second.GetRawData()); + Node *entryTo = entryMap_.FindEntry(Node::NewAddress(to)); + if (entryTo == nullptr) { + entryTo = GenerateNode(thread, it.second); + } + if (entryTo != nullptr) { + Edge *edge = Edge::NewEdge(edgeCount_, EdgeType::DEFAULT, *iter, entryTo, GetString(it.first)); + InsertEdgeUnique(edge); + (*iter)->IncEdgeCount(); // Update Node's edgeCount_ here + } + } + iter++; + } + // Fill Primitive Edge + size_t lengthExtend = nodes_.size(); + while (++count < lengthExtend) { + ASSERT(*iter != nullptr); + if ((*iter)->GetType() == NodeType::JS_PRIMITIVE_REF) { + JSTaggedValue jsFrom(reinterpret_cast((*iter)->GetAddress())); + CString valueName; + if (jsFrom.IsInt()) { + valueName.append(ToCString(jsFrom.GetInt())); + } else if (jsFrom.IsDouble()) { + valueName.append(FloatToCString(jsFrom.GetDouble())); + } else { + valueName.append("NaN"); + } + Edge *edge = Edge::NewEdge(edgeCount_, EdgeType::DEFAULT, (*iter), (*iter), GetString(valueName)); + InsertEdgeUnique(edge); + (*iter)->IncEdgeCount(); // Update Node's edgeCount_ here + } + iter++; + } +} + +void HeapSnapShot::BridgeAllReferences() +{ + // This Function is Unused + for (Edge *edge : edges_) { + auto *from = reinterpret_cast(edge->GetFrom()->GetAddress()); + auto *to = reinterpret_cast(edge->GetTo()->GetAddress()); + if (!JSTaggedValue(from).IsECMAObject()) { + continue; // named it by other way + } + edge->SetName(GenerateEdgeName(from, to)); + } +} + +CString *HeapSnapShot::GenerateEdgeName([[maybe_unused]] TaggedObject *from, [[maybe_unused]] TaggedObject *to) +{ + // This Function is Unused + ASSERT(from != nullptr && from != to); + return GetString("[]"); // unAnalysed +} + +Node *HeapSnapShot::InsertNodeUnique(Node *node) +{ + AccumulateNodeSize(node->GetSelfSize()); + nodes_.emplace_back(node); + nodeCount_++; + return node; +} + +void HeapSnapShot::EraseNodeUnique(Node *node) +{ + auto iter = std::find(nodes_.begin(), nodes_.end(), node); + if (iter != nodes_.end()) { + DecreaseNodeSize(node->GetSelfSize()); + nodes_.erase(iter); + nodeCount_--; + } +} + +Edge *HeapSnapShot::InsertEdgeUnique(Edge *edge) +{ + edges_.emplace_back(edge); + edgeCount_++; + return edge; +} + +void HeapSnapShot::AddSyntheticRoot(JSThread *thread) +{ + Node *syntheticRoot = Node::NewNode(1, nodeCount_, GetString("SyntheticRoot"), NodeType::SYNTHETIC, 0, nullptr); + InsertNodeAt(0, syntheticRoot); + + int edgeOffset = 0; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ROOT_EDGE_BUILDER_CORE(type, slot) \ + JSTaggedValue value(slot.GetTaggedType()); \ + if (value.IsHeapObject()) { \ + TaggedObject *root = value.GetTaggedObject(); \ + Node *rootNode = entryMap_.FindEntry(Node::NewAddress(root)); \ + if (rootNode != nullptr) { \ + Edge *edge = \ + Edge::NewEdge(edgeCount_, EdgeType::SHORTCUT, syntheticRoot, rootNode, GetString("-subroot-")); \ + InsertEdgeAt(edgeOffset, edge); \ + edgeOffset++; \ + syntheticRoot->IncEdgeCount(); \ + } \ + } + + RootVisitor rootEdgeBuilder = [this, syntheticRoot, &edgeOffset]([[maybe_unused]] Root type, ObjectSlot slot) { + ROOT_EDGE_BUILDER_CORE(type, slot); + }; + + RootRangeVisitor rootRangeEdgeBuilder = [this, syntheticRoot, &edgeOffset]([[maybe_unused]] Root type, + ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + ROOT_EDGE_BUILDER_CORE(type, slot); + } + }; +#undef ROOT_EDGE_BUILDER_CORE + rootVisitor_.VisitHeapRoots(thread, rootEdgeBuilder, rootRangeEdgeBuilder); + + int reindex = 0; + for (Node *node : nodes_) { + node->SetIndex(reindex); + reindex++; + } +} + +Node *HeapSnapShot::InsertNodeAt(size_t pos, Node *node) +{ + ASSERT(node != nullptr); + auto iter = nodes_.begin(); + std::advance(iter, pos); + nodes_.insert(iter, node); + nodeCount_++; + return node; +} + +Edge *HeapSnapShot::InsertEdgeAt(size_t pos, Edge *edge) +{ + ASSERT(edge != nullptr); + edges_.insert(edges_.begin() + pos, edge); + edgeCount_++; + return edge; +} + +CString EntryVisitor::ConvertKey(JSTaggedValue key) +{ + ASSERT(key.GetTaggedObject() != nullptr); + EcmaString *keyString = EcmaString::Cast(key.GetTaggedObject()); + if (key.IsSymbol()) { + JSSymbol *symbol = JSSymbol::Cast(key.GetTaggedObject()); + keyString = EcmaString::Cast(symbol->GetDescription().GetTaggedObject()); + } + // convert, expensive but safe + int length; + if (keyString->IsUtf8()) { + length = keyString->GetUtf8Length(); + std::vector buffer(length); + [[maybe_unused]] int size = keyString->CopyDataUtf8(buffer.data(), length); + ASSERT(size == length); + CString keyCopy(reinterpret_cast(buffer.data())); + return keyCopy; + } else { // NOLINT(readability-else-after-return) + length = keyString->GetLength(); + std::vector buffer(length); + [[maybe_unused]] int size = keyString->CopyDataUtf16(buffer.data(), length); + ASSERT(size == length); + CString keyCopy(reinterpret_cast(buffer.data())); + return keyCopy; + } +} + +Node *HeapEntryMap::FindOrInsertNode(Node *node) +{ + ASSERT(node != nullptr); + auto it = nodesMap_.find(node->GetAddress()); + if (it != nodesMap_.end()) { + return it->second; + } + InsertEntry(node); + return node; +} + +Node *HeapEntryMap::FindAndEraseNode(Address addr) +{ + auto it = nodesMap_.find(addr); + if (it != nodesMap_.end()) { + Node *node = it->second; + nodesMap_.erase(it); + nodeEntryCount_--; + return node; + } + return nullptr; +} + +Node *HeapEntryMap::FindEntry(Address addr) +{ + auto it = nodesMap_.find(addr); + return it != nodesMap_.end() ? it->second : nullptr; +} + +void HeapEntryMap::InsertEntry(Node *node) +{ + nodeEntryCount_++; + nodesMap_.insert(std::make_pair(node->GetAddress(), node)); +} + +FrontType NodeTypeConverter::Convert(NodeType type) +{ + FrontType fType; + if (type == NodeType::PROPERTY_BOX) { + fType = FrontType::HIDDEN; + } else if (type == NodeType::JS_ARRAY || type == NodeType::JS_TYPED_ARRAY) { + fType = FrontType::ARRAY; + } else if (type == NodeType::PRIM_STRING) { // STRING + fType = FrontType::STRING; + } else if (type == NodeType::JS_OBJECT) { + fType = FrontType::OBJECT; + } else if (type >= NodeType::JS_FUNCTION_BEGIN && type <= NodeType::JS_FUNCTION_END) { + fType = FrontType::CLOSURE; + } else if (type == NodeType::JS_BOUND_FUNCTION) { + fType = FrontType::CLOSURE; + } else if (type == NodeType::JS_FUNCTION_BASE) { + fType = FrontType::CLOSURE; + } else if (type == NodeType::JS_REG_EXP) { + fType = FrontType::REGEXP; + } else if (type == NodeType::SYMBOL) { + fType = FrontType::SYMBOL; + } else if (type == NodeType::JS_PRIMITIVE_REF) { + fType = FrontType::HEAPNUMBER; + } else if (type == NodeType::SYNTHETIC) { + fType = FrontType::SYNTHETIC; + } else { + fType = FrontType::DEFAULT; + // NATIVE, /* kNative */ + // CONSSTRING, /* kConsString */ + // SLICEDSTRING, /* kSlicedString */ + // SYMBOL, /* kSymbol */ + // BIGINT, /* kBigInt */ + } + return fType; +} +} // namespace panda::ecmascript diff --git a/ecmascript/hprof/heap_snapshot.h b/ecmascript/hprof/heap_snapshot.h new file mode 100644 index 0000000000000000000000000000000000000000..85486af28517c3ee611f5672e2af68cd292d6a10 --- /dev/null +++ b/ecmascript/hprof/heap_snapshot.h @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_ECMASCRIPT_HPROF_HEAP_SNAPSHOT_H +#define RUNTIME_ECMASCRIPT_HPROF_HEAP_SNAPSHOT_H + +#include +#include +#include +#include + +#include "ecmascript/mem/c_containers.h" +#include "os/mem.h" +#include "ecmascript/hprof/heap_profiler.h" +#include "ecmascript/hprof/heap_root_visitor.h" +#include "ecmascript/hprof/string_hashmap.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +// Define the Object Graphic +using Address = uintptr_t; + +enum class NodeType : uint8_t { + JSTYPE_DECL, + PRIM_STRING, /* Primitive String */ + PRIM_ARRAY, /* Primitive Array */ + SYNTHETIC /* For Synthetic Root */ +}; + +enum class EdgeType { CONTEXT, ELEMENT, PROPERTY, INTERNAL, HIDDEN, SHORTCUT, WEAK, DEFAULT = PROPERTY }; + +class Node { +public: + explicit Node(uint64_t id, uint64_t index, CString *name, NodeType type, size_t size, uint64_t traceId, + Address address, bool isLive = true) + : id_(id), + index_(index), + name_(name), + type_(type), + size_(size), + traceId_(traceId), + address_(address), + isLive_(isLive) + { + } + uint64_t GetId() + { + return id_; + } + void SetIndex(uint64_t index) + { + index_ = index; + } + uint64_t GetIndex() + { + return index_; + } + CString *GetName() + { + return name_; + } + NodeType GetType() + { + return type_; + } + size_t GetSelfSize() + { + return size_; + } + size_t GetEdgeCount() + { + return edgeCount_; + } + void IncEdgeCount() + { + edgeCount_++; + } + uint64_t GetStackTraceId() + { + return traceId_; + } + Address GetAddress() + { + return address_; + } + bool IsLive() + { + return isLive_; + } + void SetLive(bool isLive) + { + isLive_ = isLive; + } + static Node *NewNode(size_t id, size_t index, CString *name, NodeType type, size_t size, TaggedObject *entry, + bool isLive = true); + template + static Address NewAddress(T *addr) + { + return reinterpret_cast
(addr); + } + static constexpr int NODE_FIELD_COUNT = 7; + ~Node() = default; +private: + uint64_t id_{0}; // Range from 1 + uint64_t index_{0}; + CString *name_{nullptr}; + NodeType type_{NodeType::INVALID}; + size_t size_{0}; + size_t edgeCount_{0}; + uint64_t traceId_{0}; + Address address_{0x0}; + bool isLive_{true}; +}; + +class Edge { +public: + explicit Edge(uint64_t id, EdgeType type, Node *from, Node *to, CString *name) + : id_(id), edgeType_(type), from_(from), to_(to), name_(name) + { + } + uint64_t GetId() const + { + return id_; + } + EdgeType GetType() const + { + return edgeType_; + } + Node *GetFrom() const + { + return from_; + } + Node *GetTo() const + { + return to_; + } + CString *GetName() const + { + return name_; + } + void SetName(CString *name) + { + name_ = name; + } + void UpdateFrom(Node *node) + { + from_ = node; + } + void UpdateTo(Node *node) + { + to_ = node; + } + static Edge *NewEdge(uint64_t id, EdgeType type, Node *from, Node *to, CString *name); + static constexpr int EDGE_FIELD_COUNT = 3; + ~Edge() = default; +private: + uint64_t id_{-1ULL}; + EdgeType edgeType_{EdgeType::DEFAULT}; + Node *from_{nullptr}; + Node *to_{nullptr}; + CString *name_{nullptr}; +}; + +class TimeStamp { +public: + explicit TimeStamp(int sequenceId) : lastSequenceId_(sequenceId), timeStampUs_(TimeStamp::Now()) {} + ~TimeStamp() = default; + + DEFAULT_MOVE_SEMANTIC(TimeStamp); + DEFAULT_COPY_SEMANTIC(TimeStamp); + + int GetLastSequenceId() + { + return lastSequenceId_; + } + + int64_t GetTimeStamp() + { + return timeStampUs_; + } + +private: + static int64_t Now() + { + struct timeval tv = {0, 0}; + gettimeofday(&tv, nullptr); + const int THOUSAND = 1000; + return tv.tv_usec + tv.tv_sec * THOUSAND * THOUSAND; + } + + int lastSequenceId_{0}; + int64_t timeStampUs_{0}; +}; + +class HeapEntryMap { +public: + HeapEntryMap() = default; + ~HeapEntryMap() = default; + NO_MOVE_SEMANTIC(HeapEntryMap); + NO_COPY_SEMANTIC(HeapEntryMap); + Node *FindOrInsertNode(Node *node); + Node *FindAndEraseNode(Address addr); + Node *FindEntry(Address addr); + size_t GetCapcity() + { + return nodesMap_.size(); + } + size_t GetEntryCount() + { + return nodeEntryCount_; + } + +private: + void InsertEntry(Node *node); + size_t nodeEntryCount_{0}; + CUnorderedMap nodesMap_{}; +}; + +class HeapSnapShot { +public: + static constexpr int SEQ_STEP = 2; + NO_MOVE_SEMANTIC(HeapSnapShot); + NO_COPY_SEMANTIC(HeapSnapShot); + explicit HeapSnapShot(JSThread *thread, CAddressAllocator *allocator) + : stringTable_(allocator), thread_(thread) + { + allocator_ = allocator; + ASSERT(allocator_ != nullptr); + } + ~HeapSnapShot(); + bool BuildUp(JSThread *thread); + bool Verify(); + + void PrepareSnapShot(); + void UpdateNode(); + void AddNode(uintptr_t address); + void MoveNode(uintptr_t address, uintptr_t forward_address); + void RecordSampleTime(); + bool FinishSnapShot(); + + CVector &GetTimeStamps() + { + return timeStamps_; + } + + size_t GetNodeCount() + { + return nodeCount_; + } + size_t GetEdgeCount() + { + return edgeCount_; + } + size_t GetTotalNodeSize() + { + return totalNodesSize_; + } + void AccumulateNodeSize(size_t size) + { + totalNodesSize_ += size; + } + void DecreaseNodeSize(size_t size) + { + totalNodesSize_ -= size; + } + CString *GenerateNodeName(JSThread *thread, TaggedObject *entry); + NodeType GenerateNodeType(TaggedObject *entry); + CList *GetNodes() + { + return &nodes_; + } + CVector *GetEdges() + { + return &edges_; + } + StringHashMap *GetEcmaStringTable() + { + return &stringTable_; + } + + static CAddressAllocator *GetAllocator(); + CString *GetString(const CString &as); + +private: + void FillNodes(JSThread *thread); + Node *GenerateNode(JSThread *thread, JSTaggedValue entry, int sequenceId = -1); + Node *GenerateStringNode(JSTaggedValue entry, int sequenceId); + void FillEdges(JSThread *thread); + void BridgeAllReferences(); + CString *GenerateEdgeName(TaggedObject *from, TaggedObject *to); + + Node *InsertNodeUnique(Node *node); + void EraseNodeUnique(Node *node); + Edge *InsertEdgeUnique(Edge *edge); + void AddSyntheticRoot(JSThread *thread); + Node *InsertNodeAt(size_t pos, Node *node); + Edge *InsertEdgeAt(size_t pos, Edge *edge); + + static CAddressAllocator *allocator_; + StringHashMap stringTable_; + CList nodes_{}; + CVector edges_{}; + CVector timeStamps_{}; + std::atomic_int sequenceId_{1}; // 1 Reversed for SyntheticRoot + int nodeCount_{0}; + int edgeCount_{0}; + int totalNodesSize_{0}; + HeapEntryMap entryMap_; + panda::ecmascript::HeapRootVisitor rootVisitor_; + JSThread *thread_; +}; + +class EntryVisitor { +public: + NO_MOVE_SEMANTIC(EntryVisitor); + NO_COPY_SEMANTIC(EntryVisitor); + explicit EntryVisitor() = default; + ~EntryVisitor() = default; + static CString ConvertKey(JSTaggedValue key); +}; + +enum class FrontType { + HIDDEN, /* kHidden */ + ARRAY, /* kArray */ + STRING, /* kString */ + OBJECT, /* kObject */ + CODE, /* kCode */ + CLOSURE, /* kClosure */ + REGEXP, /* kRegExp */ + HEAPNUMBER, /* kHeapNumber */ + NATIVE, /* kNative */ + SYNTHETIC, /* kSynthetic */ + CONSSTRING, /* kConsString */ + SLICEDSTRING, /* kSlicedString */ + SYMBOL, /* kSymbol */ + BIGINT, /* kBigInt */ + DEFAULT = NATIVE, /* kDefault */ +}; + +class NodeTypeConverter { +public: + explicit NodeTypeConverter() = default; + ~NodeTypeConverter() = default; + NO_MOVE_SEMANTIC(NodeTypeConverter); + NO_COPY_SEMANTIC(NodeTypeConverter); + /* + * For Front-End to Show Statistics Correctly + */ + static FrontType Convert(NodeType type); +}; +} // namespace panda::ecmascript +#endif // RUNTIME_ECMASCRIPT_HPROF_HEAP_SNAPSHOT_H diff --git a/ecmascript/hprof/heap_snapshot_json_serializer.cpp b/ecmascript/hprof/heap_snapshot_json_serializer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f302785e1dfe128af1560ba91514c30cf05403d0 --- /dev/null +++ b/ecmascript/hprof/heap_snapshot_json_serializer.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/hprof/heap_snapshot.h" +#include "ecmascript/hprof/heap_snapshot_json_serializer.h" +#include "ecmascript/hprof/string_hashmap.h" + +namespace panda::ecmascript { +bool HeapSnapShotJSONSerializer::Serialize(HeapSnapShot *snapShot, const CString &fileName) +{ + // Serialize Node/Edge/String-Table + snapShot_ = snapShot; + ASSERT(snapShot_->GetNodes() != nullptr && snapShot_->GetEdges() != nullptr && + snapShot_->GetEcmaStringTable() != nullptr); + stringBuffer_.str(""); // Clear Buffer + + SerializeSnapShotHeader(); // 1. + SerializeNodes(); // 2. + SerializeEdges(); // 3. + SerializeTraceFunctionInfo(); // 4. + SerializeTraceTree(); // 5. + SerializeSamples(); // 6. + SerializeLocations(); // 7. + SerializeStringTable(); // 8. + SerializerSnapShotClosure(); // 9. + + WriteJSON(fileName); // 10. + return true; +} + +void HeapSnapShotJSONSerializer::SerializeSnapShotHeader() +{ + stringBuffer_ << "{\"snapshot\":\n"; // 1. + stringBuffer_ << "{\"meta\":\n"; // 2. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "{\"node_fields\":[\"type\",\"name\",\"id\",\"self_size\",\"edge_count\",\"trace_node_id\","; + stringBuffer_ << "\"detachedness\"],\n"; // 3. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"node_types\":[[\"hidden\",\"array\",\"string\",\"object\",\"code\",\"closure\",\"regexp\","; + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"number\",\"native\",\"synthetic\",\"concatenated string\",\"slicedstring\",\"symbol\","; + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"bigint\"],\"string\",\"number\",\"number\",\"number\",\"number\",\"number\"],\n"; // 4. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"edge_fields\":[\"type\",\"name_or_index\",\"to_node\"],\n"; // 5. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"edge_types\":[[\"context\",\"element\",\"property\",\"internal\",\"hidden\",\"shortcut\","; + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"weak\"],\"string_or_number\",\"node\"],\n"; // 6. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\","; + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"line\",\"column\"],\n"; // 7. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],\n"; + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],\n"; // 9. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]},\n"; // 10. + stringBuffer_ << "\"node_count\":" << snapShot_->GetNodeCount() << ",\n"; // 11. + stringBuffer_ << "\"edge_count\":" << snapShot_->GetEdgeCount() << ",\n"; // 12. + stringBuffer_ << "\"trace_function_count\":" + << "0\n"; // 13. + stringBuffer_ << "},\n"; // 14. +} + +void HeapSnapShotJSONSerializer::SerializeNodes() +{ + CList *nodes = snapShot_->GetNodes(); + StringHashMap *stringTable = snapShot_->GetEcmaStringTable(); + ASSERT(nodes != nullptr); + stringBuffer_ << "\"nodes\":["; // Section Header + size_t i = 0; + for (auto *node : *nodes) { + if (i > 0) { + stringBuffer_ << ","; // add comma except first line + } + stringBuffer_ << static_cast(NodeTypeConverter::Convert(node->GetType())) << ","; // 1. + stringBuffer_ << stringTable->GetStringId(node->GetName()) << ","; // 2. + stringBuffer_ << node->GetId() << ","; // 3. + stringBuffer_ << node->GetSelfSize() << ","; // 4. + stringBuffer_ << node->GetEdgeCount() << ","; // 5. + stringBuffer_ << node->GetStackTraceId() << ","; // 6. + if (i == nodes->size() - 1) { // add comma at last the line + stringBuffer_ << "0" + << "],\n"; // 7. detachedness default + } else { + stringBuffer_ << "0\n"; // 7. + } + i++; + } +} + +void HeapSnapShotJSONSerializer::SerializeEdges() +{ + CVector *edges = snapShot_->GetEdges(); + StringHashMap *stringTable = snapShot_->GetEcmaStringTable(); + ASSERT(edges != nullptr); + stringBuffer_ << "\"edges\":["; + size_t i = 0; + for (auto *edge : *edges) { + if (i > 0) { // add comma except the first line + stringBuffer_ << ","; + } + stringBuffer_ << static_cast(edge->GetType()) << ","; // 1. + stringBuffer_ << stringTable->GetStringId(edge->GetName()) << ","; // 2. Use StringId + + if (i == edges->size() - 1) { // add comma at last the line + stringBuffer_ << edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT << "],\n"; // 3. + } else { + stringBuffer_ << edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT << "\n"; // 3. + } + i++; + } +} + +void HeapSnapShotJSONSerializer::SerializeTraceFunctionInfo() +{ + stringBuffer_ << "\"trace_function_infos\":[],\n"; // Empty +} + +void HeapSnapShotJSONSerializer::SerializeTraceTree() +{ + stringBuffer_ << "\"trace_tree\":[],\n"; // Empty +} + +void HeapSnapShotJSONSerializer::SerializeSamples() +{ + stringBuffer_ << "\"samples\":["; + CVector &timeStamps = snapShot_->GetTimeStamps(); + if (!timeStamps.empty()) { + auto firstTimeStamp = timeStamps[0]; + bool isFirst = true; + for (auto timeStamp : timeStamps) { + if (!isFirst) { + stringBuffer_ << "\n, "; + } else { + isFirst = false; + } + stringBuffer_ << timeStamp.GetTimeStamp() - firstTimeStamp.GetTimeStamp() << ", "; + stringBuffer_ << timeStamp.GetLastSequenceId(); + } + } + stringBuffer_ << "],\n"; +} + +void HeapSnapShotJSONSerializer::SerializeLocations() +{ + stringBuffer_ << "\"locations\":[],\n"; +} + +void HeapSnapShotJSONSerializer::SerializeStringTable() +{ + StringHashMap *stringTable = snapShot_->GetEcmaStringTable(); + ASSERT(stringTable != nullptr); + stringBuffer_ << "\"strings\":[\"\",\n"; + stringBuffer_ << "\"\",\n"; + stringBuffer_ << "\"GC roots\",\n"; + // StringId Range from 3 + size_t capcity = stringTable->GetCapcity(); + size_t i = 0; + for (auto key : *(stringTable->GetOrderedKeyStorage())) { + if (i == capcity - 1) { + stringBuffer_ << "\"" << *(stringTable->GetStringByKey(key)) << "\"\n"; // No Comma for the last line + } else { + stringBuffer_ << "\"" << *(stringTable->GetStringByKey(key)) << "\",\n"; + } + i++; + } + stringBuffer_ << "]\n"; +} + +void HeapSnapShotJSONSerializer::SerializerSnapShotClosure() +{ + stringBuffer_ << "}\n"; +} + +void HeapSnapShotJSONSerializer::WriteJSON(const CString &fileName) +{ + std::string fName(fileName); + outputStream_.open(fName, std::ios::out); + if (!outputStream_.good()) { + LOG_ECMA(ERROR) << "open file failed"; + return; + } + outputStream_ << stringBuffer_.str(); + outputStream_.close(); + outputStream_.clear(); // Make sure the next open operation success +} +} // namespace panda::ecmascript diff --git a/ecmascript/hprof/heap_snapshot_json_serializer.h b/ecmascript/hprof/heap_snapshot_json_serializer.h new file mode 100644 index 0000000000000000000000000000000000000000..91d8b6abcb338966cb90df6f38a9fa1afca9013e --- /dev/null +++ b/ecmascript/hprof/heap_snapshot_json_serializer.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_ECMASCRIPT_HPROF_HEAP_SNAPSHOT_SERIALIZER_H +#define RUNTIME_ECMASCRIPT_HPROF_HEAP_SNAPSHOT_SERIALIZER_H + +#include +#include + +#include "ecmascript/mem/c_string.h" +#include "os/mem.h" + +namespace panda::ecmascript { +using fstream = std::fstream; +using stringstream = std::stringstream; + +class HeapSnapShot; + +class HeapSnapShotJSONSerializer { +public: + explicit HeapSnapShotJSONSerializer() = default; + ~HeapSnapShotJSONSerializer() = default; + NO_MOVE_SEMANTIC(HeapSnapShotJSONSerializer); + NO_COPY_SEMANTIC(HeapSnapShotJSONSerializer); + bool Serialize(HeapSnapShot *snapShot, const CString &fileName); + +private: + void SerializeSnapShotHeader(); + void SerializeNodes(); + void SerializeEdges(); + void SerializeTraceFunctionInfo(); + void SerializeTraceTree(); + void SerializeSamples(); + void SerializeLocations(); + void SerializeStringTable(); + void SerializerSnapShotClosure(); + + void WriteJSON(const CString &fileName); + fstream outputStream_; + HeapSnapShot *snapShot_{nullptr}; + stringstream stringBuffer_; +}; +} // namespace panda::ecmascript +#endif // RUNTIME_ECMASCRIPT_HPROF_HEAP_SNAPSHOT_SERIALIZER_H diff --git a/ecmascript/hprof/heap_tracker.cpp b/ecmascript/hprof/heap_tracker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..75e00fa2ac766114f5ee6b70c5263c96053e27de --- /dev/null +++ b/ecmascript/hprof/heap_tracker.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/hprof/heap_tracker.h" +#include "ecmascript/hprof/heap_snapshot.h" +#include "ecmascript/mem/space.h" + +namespace panda::ecmascript { +static constexpr int32_t MILLI_TO_MICRO = 1000; + +void HeapTrackerSample::Run() +{ + while (!isInterrupt_) { + snapShot_->RecordSampleTime(); + usleep(timeInterval_ * MILLI_TO_MICRO); + } +} + +void HeapTracker::AllocationEvent(uintptr_t address) +{ + if (snapShot_ != nullptr) { + snapShot_->AddNode(address); + } +} + +void HeapTracker::MoveEvent(uintptr_t address, uintptr_t forward_address) +{ + if (snapShot_ != nullptr) { + snapShot_->MoveNode(address, forward_address); + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/hprof/heap_tracker.h b/ecmascript/hprof/heap_tracker.h new file mode 100644 index 0000000000000000000000000000000000000000..199ab8bb68b69d6250186e5e1f11f89fcf7c3266 --- /dev/null +++ b/ecmascript/hprof/heap_tracker.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_HPROF_HEAP_TRACKER_H +#define PANDA_RUNTIME_ECMASCRIPT_HPROF_HEAP_TRACKER_H + +#include +#include +#include + +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +class HeapSnapShot; + +class HeapTrackerSample { +public: + explicit HeapTrackerSample(HeapSnapShot *snapShot, double timeInterval) + : timeInterval_(timeInterval), snapShot_(snapShot) + { + } + + ~HeapTrackerSample() + { + isInterrupt_ = true; + } + + void Start() + { + isInterrupt_ = false; + thread_ = std::thread(&HeapTrackerSample::Run, this); + } + + void Stop() + { + isInterrupt_ = true; + if (thread_.joinable()) { + thread_.join(); + } + } + + void Run(); + + NO_COPY_SEMANTIC(HeapTrackerSample); + NO_MOVE_SEMANTIC(HeapTrackerSample); + +private: + std::thread thread_; + std::atomic_bool isInterrupt_ = true; + double timeInterval_ = 0; + HeapSnapShot *snapShot_; +}; + +class HeapTracker { +public: + HeapTracker(HeapSnapShot *snapShot, double timeInterval) : snapShot_(snapShot), sample_(snapShot, timeInterval) {} + ~HeapTracker() = default; + + void StartTracing() + { + sample_.Start(); + } + + void StopTracing() + { + sample_.Stop(); + } + + void AllocationEvent(uintptr_t address); + void MoveEvent(uintptr_t address, uintptr_t forward_address); + + NO_COPY_SEMANTIC(HeapTracker); + NO_MOVE_SEMANTIC(HeapTracker); + +private: + HeapSnapShot *snapShot_; + HeapTrackerSample sample_; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_HPROF_HEAP_TRACKER_H diff --git a/ecmascript/hprof/string_hashmap.cpp b/ecmascript/hprof/string_hashmap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8c713b336834896ac901b637d149bc1d071e81d8 --- /dev/null +++ b/ecmascript/hprof/string_hashmap.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/hprof/string_hashmap.h" + +namespace panda::ecmascript { +CString *StringHashMap::FindOrInsertString(CString *string) +{ + StringKey key = GenerateStringKey(string); + auto it = hashmap_.find(key); + if (it != hashmap_.end()) { + return it->second; + } else { // NOLINT(readability-else-after-return) + index_++; + hashmap_.insert(std::make_pair(key, string)); + orderedKey_.emplace_back(key); + indexMap_.insert(std::make_pair(key, index_)); + return string; + } +} + +StringId StringHashMap::GetStringId(CString *string) +{ + auto it = indexMap_.find(GenerateStringKey(string)); + return it != indexMap_.end() ? it->second : 1; // "" +} + +CString *StringHashMap::GetStringByKey(StringKey key) +{ + auto it = hashmap_.find(key); + if (it != hashmap_.end()) { + return FormatString(it->second); + } + return nullptr; +} + +CString *StringHashMap::FormatString(CString *string) +{ + // remove "\"" | "\r\n" | "\\" | "\t" | "\f" + int length = string->length(); + char *charSeq = const_cast(string->c_str()); + for (int i = 0; i < length; i++) { + if (charSeq[i] == '\"') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (charSeq[i] == '\r') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (charSeq[i] == '\n') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (charSeq[i] == '\\') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (charSeq[i] == '\t') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (charSeq[i] == '\f') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (charSeq[i] < ' ') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + // ctrl chars 0~31 + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } + *string = charSeq; + return string; +} + +StringKey StringHashMap::GenerateStringKey(CString *string) +{ + return std::hash{}(std::string(*string)); +} + +CString *StringHashMap::GetString(CString as) +{ + auto *tempString = allocator_->New(std::move(as)); + CString *oldString = FindOrInsertString(tempString); + if (tempString != oldString) { + allocator_->Finalize(tempString); + return oldString; + } + return tempString; +} + +void StringHashMap::Clear() +{ + for (auto it : hashmap_) { + if (it.second != nullptr) { + allocator_->Finalize(it.second); + } + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/hprof/string_hashmap.h b/ecmascript/hprof/string_hashmap.h new file mode 100644 index 0000000000000000000000000000000000000000..c5a9349021960a5046eb1787b4b9de63aadef71b --- /dev/null +++ b/ecmascript/hprof/string_hashmap.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_ECMASCRIPT_HPROF_STRING_HASHMAP_H +#define RUNTIME_ECMASCRIPT_HPROF_STRING_HASHMAP_H + +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/mem/c_string.h" +#include "os/mem.h" + +namespace panda::ecmascript { +using StringKey = uint64_t; +using StringId = uint64_t; + +// An Implementation for Native StringTable without Auto Mem-Management +// To make sure when using String, it still stays where it was. +class StringHashMap { +public: + explicit StringHashMap(CAddressAllocator *allocator) : allocator_(allocator) + { + ASSERT(allocator_ != nullptr); + } + ~StringHashMap() + { + Clear(); + } + NO_MOVE_SEMANTIC(StringHashMap); + NO_COPY_SEMANTIC(StringHashMap); + /* + * The ID is the seat number in JSON file Range from 0~string_table_.size() + */ + StringId GetStringId(CString *string); + /* + * Get all keys sorted by insert order + */ + CVector *GetOrderedKeyStorage() + { + return &orderedKey_; + } + /* + * Get string by its hash key + */ + CString *GetStringByKey(StringKey key); + size_t GetCapcity() + { + ASSERT(orderedKey_.size() == hashmap_.size()); + return orderedKey_.size(); + } + /* + * For external call to use this StringTable + */ + CString *GetString(CString as); + +private: + StringKey GenerateStringKey(CString *string); + CString *FindOrInsertString(CString *string); + CString *FormatString(CString *string); + /* + * Free all memory + */ + void Clear(); + CAddressAllocator *allocator_{nullptr}; + CVector orderedKey_; // Used for Serialize Order + size_t index_{2}; // 2: Offset the String-Table Header + CUnorderedMap indexMap_; + CUnorderedMap hashmap_; +}; +} // namespace panda::ecmascript +#endif // RUNTIME_ECMASCRIPT_HPROF_STRING_HASHMAP_H diff --git a/ecmascript/hprof/tests/BUILD.gn b/ecmascript/hprof/tests/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..e2883f049d6791c0930c7b57a8cc11a2e1e00392 --- /dev/null +++ b/ecmascript/hprof/tests/BUILD.gn @@ -0,0 +1,78 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//ark/js_runtime/test/test_helper.gni") +import("//build/test.gni") + +module_output_path = "ark/js_runtime" + +host_unittest_action("HeapTrackerTest") { + module_out_path = module_output_path + + sources = [ + # test file + "heap_tracker_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("HprofTest") { + module_out_path = module_output_path + + sources = [ + # test file + "hprof_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +group("unittest") { + testonly = true + deps = [ + ":HeapTrackerTest", + ":HprofTest", + ] +} + +group("host_unittest") { + testonly = true + deps = [ + ":HeapTrackerTestAction(${host_toolchain})", + ":HprofTestAction(${host_toolchain})", + ] +} diff --git a/ecmascript/hprof/tests/heap_tracker_test.cpp b/ecmascript/hprof/tests/heap_tracker_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..09ce8a6e829237e80c4120518b40cd9ad9372325 --- /dev/null +++ b/ecmascript/hprof/tests/heap_tracker_test.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "ecmascript/ecma_string.h" +#include "ecmascript/global_env.h" +#include "ecmascript/hprof/heap_profiler_interface.h" +#include "ecmascript/hprof/heap_snapshot_json_serializer.h" + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class HeapTrackerTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + EcmaVM::Cast(instance)->GetFactory()->SetTriggerGc(false); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(HeapTrackerTest, HeapTracker) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + HeapProfilerInterface *heapProfile = HeapProfilerInterface::CreateHeapProfiler(thread); + heapProfile->StartHeapTracking(thread, 50); + sleep(1); + int count = 100; + while (count-- > 0) { + thread->GetEcmaVM()->GetFactory()->NewJSAsyncFuncObject(); + } + sleep(1); + count = 100; + while (count-- > 0) { + thread->GetEcmaVM()->GetFactory()->NewJSSymbol(); + } + sleep(1); + count = 100; + while (count-- > 0) { + JSHandle string = thread->GetEcmaVM()->GetFactory()->NewFromString("Hello World"); + thread->GetEcmaVM()->GetFactory()->NewJSString(JSHandle(string)); + } + + // Create file test.heaptimeline + std::string fileName = "test.heaptimeline"; + fstream outputString(fileName, std::ios::out); + outputString.close(); + outputString.clear(); + + heapProfile->StopHeapTracking(thread, DumpFormat::JSON, fileName.c_str()); + HeapProfilerInterface::Destory(thread, heapProfile); + + // Check + fstream inputStream(fileName, std::ios::in); + std::string line; + std::string emptySample = "\"samples\":"; + std::string firstSample = "\"samples\":[0, "; + int emptySize = emptySample.size(); + bool isFind = false; + while (getline(inputStream, line)) { + if (line.substr(0, emptySize) == emptySample) { + ASSERT_TRUE(line.substr(0, firstSample.size()) == firstSample); + isFind = true; + } + } + ASSERT_TRUE(isFind); + + inputStream.close(); + inputStream.clear(); + std::remove(fileName.c_str()); +} +} // namespace panda::test diff --git a/ecmascript/hprof/tests/hprof_test.cpp b/ecmascript/hprof/tests/hprof_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a8edc9b592ddda2de6e6be447a6fa1df68f5a051 --- /dev/null +++ b/ecmascript/hprof/tests/hprof_test.cpp @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/hprof/heap_profiler.h" +#include "ecmascript/hprof/heap_profiler_interface.h" +#include "ecmascript/hprof/heap_snapshot.h" +#include "ecmascript/hprof/heap_snapshot_json_serializer.h" +#include "ecmascript/hprof/string_hashmap.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class HProfTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +class HProfTestHelper { +public: + explicit HProfTestHelper(const CString &aFilePath) : filePath(aFilePath) {} + ~HProfTestHelper() = default; + + bool IsFileExist() + { + bool exist = false; + exist = SafeOpenFile(); + SafeCloseFile(); + return exist; + } + + bool RemoveExistingFile() + { + int code = 0; + if (IsFileExist()) { + code = std::remove(filePath.c_str()); + } + return code == 0 ? true : false; + } + + bool GenerateHeapDump(JSThread *thread) + { + int retry = 2; + RemoveExistingFile(); + HeapProfilerInterface::DumpHeapSnapShot(thread, DumpFormat::JSON, filePath); + while (!IsFileExist() && retry > 0) { + retry--; + HeapProfilerInterface::DumpHeapSnapShot(thread, DumpFormat::JSON, filePath); + } + return IsFileExist(); + } + + bool ContrastJSONLineHeader(int lineId, CString lineHeader) + { + bool allSame = false; + if (!SafeOpenFile()) { // No JSON File + allSame = false; + SafeCloseFile(); + } else { + CString line; + int i = 1; + while (getline(inputFile, line)) { + if (i == lineId && line.find(lineHeader) != line.npos) { + allSame = true; + break; + } + i++; + } + SafeCloseFile(); + } + + return allSame; + } + + bool ContrastJSONSectionPayload(CString dataLable, int fieldNum) + { + if (!SafeOpenFile()) { // No JSON File + SafeCloseFile(); + return false; + } else { + CString line; + int i = 1; + while (getline(inputFile, line)) { + if (i > 10 && line.find(dataLable) != line.npos) { // 10 : Hit the line + CString::size_type pos = 0; + int loop = 0; + while ((pos = line.find(",", pos)) != line.npos) { + pos++; + loop++; // "," count + } + SafeCloseFile(); + return loop == fieldNum - 1; + break; + } + i++; // Search the Next Line + } + SafeCloseFile(); + } + return false; // Lost the Line + } + + bool ContrastJSONClousure() + { + if (!SafeOpenFile()) { // No JSON File + SafeCloseFile(); + return false; + } + CString lineBk; // The Last Line + CString line; + while (getline(inputFile, line)) { + lineBk = line; + } + SafeCloseFile(); + return lineBk.compare("}") == 0; + } + + int ExtractCountFromMeta(CString typeLable) + { + if (!SafeOpenFile()) { // No JSON File + SafeCloseFile(); + return -1; + } else { + CString line; + int i = 1; + while (getline(inputFile, line)) { + int length = line.length() - typeLable.length() - 1; + if (line.find(typeLable) != line.npos) { // Get + if (line.find(",") == line.npos) { // "trace_function_count" end without "," + length = line.length() - typeLable.length(); + } + line = line.substr(typeLable.length(), length); + SafeCloseFile(); + return std::stoi(line.c_str()); + } + i++; + } + SafeCloseFile(); + } + return -1; + } + + int ExtractCountFromPayload(CString dataLabel) + { + if (!SafeOpenFile()) { // No JSON File + SafeCloseFile(); + return -1; + } else { + CString line; + bool hit = false; + int loop = 0; + while (getline(inputFile, line)) { + if (!hit && line.find(dataLabel) != line.npos) { // Get + loop += 1; // First Line + hit = true; + if (line.find("[]") != line.npos) { // Empty + loop = 0; + SafeCloseFile(); + return loop; + } else { + continue; + } + } + if (hit) { + if (line.find("],") != line.npos) { // Reach End + loop += 1; // End Line + SafeCloseFile(); + return loop; + } else { + loop++; + continue; + } + } + } + SafeCloseFile(); + } + return -1; + } + +private: + bool SafeOpenFile() + { + inputFile.clear(); + inputFile.open(filePath.c_str(), std::ios::in); + int retry = 2; + while (!inputFile.good() && retry > 0) { + inputFile.open(filePath.c_str(), std::ios::in); + retry--; + } + return inputFile.good(); + } + + void SafeCloseFile() + { + inputFile.close(); + inputFile.clear(); // Reset File status + } + CString filePath; + std::fstream inputFile {}; +}; + +HWTEST_F_L0(HProfTest, GenerateFileForManualCheck) +{ + HProfTestHelper tester("hprof_json_test.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); +} + +HWTEST_F_L0(HProfTest, GenerateFile) +{ + HProfTestHelper tester("GenerateFile.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +HWTEST_F_L0(HProfTest, ParseJSONHeader) +{ + HProfTestHelper tester("ParseJSONHeader.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONLineHeader(1, "{\"snapshot\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(2, "{\"meta\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(3, "{\"node_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(4, "\"node_types\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(5, "\"edge_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(6, "\"edge_types\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(7, "\"trace_function_info_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(8, "\"trace_node_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(9, "\"sample_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(10, "\"location_fields\":")); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +HWTEST_F_L0(HProfTest, ContrastTraceFunctionInfo) +{ + HProfTestHelper tester("ContrastTraceFunctionInfo.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"trace_function_infos\":", 2)); // Empty + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +HWTEST_F_L0(HProfTest, ContrastTraceTree) +{ + HProfTestHelper tester("ContrastTraceTree.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"trace_tree\":", 2)); // Empty + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +HWTEST_F_L0(HProfTest, ContrastSamples) +{ + HProfTestHelper tester("ContrastSamples.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"samples\":", 2)); // Empty + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +HWTEST_F_L0(HProfTest, ContrastLocations) +{ + HProfTestHelper tester("ContrastLocations.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"locations\":", 2)); // Empty + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +HWTEST_F_L0(HProfTest, ContrastString) +{ + HProfTestHelper tester("ContrastString.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"strings\":[", 1 + 1)); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +HWTEST_F_L0(HProfTest, ContrastClosure) +{ + HProfTestHelper tester("ContrastClosure.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONClousure()); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +HWTEST_F_L0(HProfTest, ContrastEdgeCount) +{ + HProfTestHelper tester("ContrastEdgeCount.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ExtractCountFromMeta("\"edge_count\":") == tester.ExtractCountFromPayload("\"edges\":[")); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +HWTEST_F_L0(HProfTest, ContrastTraceFunctionInfoCount) +{ + HProfTestHelper tester("ContrastTraceFunctionInfoCount.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ExtractCountFromMeta("\"trace_function_count\":") == + tester.ExtractCountFromPayload("\"trace_function_infos\":")); + ASSERT_TRUE(tester.RemoveExistingFile()); +} +} // namespace panda::test diff --git a/ecmascript/ic/fast_ic_runtime_stub-inl.h b/ecmascript/ic/fast_ic_runtime_stub-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..d58c3d73ad9527734e71cdcc803a5fdea0a60f0e --- /dev/null +++ b/ecmascript/ic/fast_ic_runtime_stub-inl.h @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_FAST_IC_RUNTIME_STUB_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_FAST_IC_RUNTIME_STUB_INL_H + +#include "fast_ic_runtime_stub.h" +#include "ic_handler.h" +#include "ic_runtime.h" +#include "profile_type_info.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/global_dictionary-inl.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_proxy.h" +#include "ecmascript/global_env.h" +#include "ecmascript/object_factory-inl.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/ic/proto_change_details.h" + +namespace panda::ecmascript { +JSTaggedValue ICRuntimeStub::LoadGlobalICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue globalValue, JSTaggedValue key, uint32_t slotId) +{ + JSTaggedValue handler = profileTypeInfo->Get(slotId); + if (handler.IsHeapObject()) { + auto result = LoadGlobal(handler); + if (!result.IsHole()) { + return result; + } + } + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, globalValue); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + LoadICRuntime icRuntime(thread, JSHandle::Cast(profileInfoHandle), slotId, + ICKind::NamedGlobalLoadIC); + return icRuntime.LoadMiss(receiverHandle, keyHandle); +} + +JSTaggedValue ICRuntimeStub::StoreGlobalICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue globalValue, JSTaggedValue key, + JSTaggedValue value, uint32_t slotId) +{ + JSTaggedValue handler = profileTypeInfo->Get(slotId); + if (handler.IsHeapObject()) { + auto result = StoreGlobal(thread, value, handler); + if (!result.IsHole()) { + return result; + } + } + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, globalValue); + auto valueHandle = JSHandle(thread, value); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + StoreICRuntime icRuntime(thread, JSHandle::Cast(profileInfoHandle), slotId, + ICKind::NamedGlobalStoreIC); + return icRuntime.StoreMiss(receiverHandle, keyHandle, valueHandle); +} + +JSTaggedValue ICRuntimeStub::CheckPolyHClass(JSTaggedValue cachedValue, JSHClass* hclass) +{ + if (!cachedValue.IsWeak()) { + ASSERT(cachedValue.IsTaggedArray()); + TaggedArray *array = TaggedArray::Cast(cachedValue.GetHeapObject()); + array_size_t length = array->GetLength(); + for (array_size_t i = 0; i < length; i += 2) { // 2 means one ic, two slot + if (array->Get(i).GetWeakReferent() == hclass) { + return array->Get(i + 1); + } + } + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue ICRuntimeStub::LoadICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue receiver, JSTaggedValue key, uint32_t slotId) +{ + if (receiver.IsHeapObject()) { + JSTaggedValue cachedValue = profileTypeInfo->Get(slotId); + if (cachedValue.IsHeapObject()) { + auto hclass = receiver.GetTaggedObject()->GetClass(); + if (cachedValue.GetWeakReferentUnChecked() == hclass) { + auto cachedHandler = profileTypeInfo->Get(slotId + 1); + auto result = LoadICWithHandler(thread, receiver, receiver, cachedHandler); + if (!result.IsHole()) { + return result; + } + } + JSTaggedValue cachedHandler = CheckPolyHClass(cachedValue, hclass); + if (!cachedHandler.IsHole()) { + auto result = LoadICWithHandler(thread, receiver, receiver, cachedHandler); + if (!result.IsHole()) { + return result; + } + } + } else if (cachedValue.IsHole()) { + JSTaggedValue result = FastRuntimeStub::GetPropertyByName(thread, receiver, key); + if (!result.IsHole()) { + return result; + } + } + } + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, receiver); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + LoadICRuntime icRuntime(thread, JSHandle::Cast(profileInfoHandle), slotId, ICKind::NamedLoadIC); + return icRuntime.LoadMiss(receiverHandle, keyHandle); +} + +JSTaggedValue ICRuntimeStub::LoadICByValue(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue receiver, JSTaggedValue key, uint32_t slotId) +{ + if (receiver.IsHeapObject()) { + JSTaggedValue cachedValue = profileTypeInfo->Get(slotId); + if (cachedValue.IsHeapObject()) { + auto hclass = receiver.GetTaggedObject()->GetClass(); + if (cachedValue.GetWeakReferentUnChecked() == hclass) { + ASSERT(HandlerBase::IsElement(profileTypeInfo->Get(slotId + 1).GetInt())); + auto result = LoadElement(JSObject::Cast(receiver.GetHeapObject()), key); + if (!result.IsHole()) { + return result; + } + } + // Check key + if (cachedValue == key) { + cachedValue = profileTypeInfo->Get(slotId + 1); + JSTaggedValue cachedHandler = CheckPolyHClass(cachedValue, hclass); + if (!cachedHandler.IsHole()) { + auto result = LoadICWithHandler(thread, receiver, receiver, cachedHandler); + if (!result.IsHole()) { + return result; + } + } + } + } else if (cachedValue.IsHole()) { + JSTaggedValue result = FastRuntimeStub::GetPropertyByValue(thread, receiver, key); + if (!result.IsHole()) { + return result; + } + } + } + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, receiver); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + LoadICRuntime icRuntime(thread, JSHandle::Cast(profileInfoHandle), slotId, ICKind::LoadIC); + return icRuntime.LoadMiss(receiverHandle, keyHandle); +} + +JSTaggedValue ICRuntimeStub::StoreICByValue(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver, + JSTaggedValue key, JSTaggedValue value, uint32_t slotId) +{ + if (receiver.IsHeapObject()) { + JSTaggedValue cachedValue = profileTypeInfo->Get(slotId); + if (cachedValue.IsHeapObject()) { + auto hclass = receiver.GetTaggedObject()->GetClass(); + if (cachedValue.GetWeakReferentUnChecked() == hclass) { + auto handlerInfo = static_cast(profileTypeInfo->Get(slotId + 1).GetInt()); + auto result = StoreElement(thread, JSObject::Cast(receiver.GetHeapObject()), key, value, handlerInfo); + if (!result.IsHole()) { + return result; + } + } + // Check key + if (cachedValue == key) { + cachedValue = profileTypeInfo->Get(slotId + 1); + JSTaggedValue cachedHandler = CheckPolyHClass(cachedValue, hclass); + if (!cachedHandler.IsHole()) { + auto result = StoreICWithHandler(thread, receiver, receiver, value, cachedHandler); + if (!result.IsHole()) { + return result; + } + } + } + } else if (cachedValue.IsHole()) { + JSTaggedValue result = FastRuntimeStub::SetPropertyByValue(thread, receiver, key, value); + if (!result.IsHole()) { + return result; + } + } + } + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, receiver); + auto valueHandle = JSHandle(thread, value); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + StoreICRuntime icRuntime(thread, JSHandle::Cast(profileInfoHandle), slotId, ICKind::StoreIC); + return icRuntime.StoreMiss(receiverHandle, keyHandle, valueHandle); +} + +JSTaggedValue ICRuntimeStub::StoreICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver, + JSTaggedValue key, JSTaggedValue value, uint32_t slotId) +{ + if (receiver.IsHeapObject()) { + JSTaggedValue cachedValue = profileTypeInfo->Get(slotId); + if (cachedValue.IsHeapObject()) { + auto hclass = receiver.GetTaggedObject()->GetClass(); + if (cachedValue.GetWeakReferentUnChecked() == hclass) { + auto cachedHandler = profileTypeInfo->Get(slotId + 1); + auto result = StoreICWithHandler(thread, receiver, receiver, value, cachedHandler); + if (!result.IsHole()) { + return result; + } + } + JSTaggedValue cachedHandler = CheckPolyHClass(cachedValue, hclass); + if (!cachedHandler.IsHole()) { + auto result = StoreICWithHandler(thread, receiver, receiver, value, cachedHandler); + if (!result.IsHole()) { + return result; + } + } + } else if (cachedValue.IsHole()) { + JSTaggedValue result = FastRuntimeStub::SetPropertyByName(thread, receiver, key, value); + if (!result.IsHole()) { + return result; + } + } + } + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, receiver); + auto valueHandle = JSHandle(thread, value); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + StoreICRuntime icRuntime(thread, JSHandle::Cast(profileInfoHandle), slotId, ICKind::NamedStoreIC); + return icRuntime.StoreMiss(receiverHandle, keyHandle, valueHandle); +} + +JSTaggedValue ICRuntimeStub::StoreICWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, + JSTaggedValue value, JSTaggedValue handler) +{ + if (handler.IsInt()) { + auto handlerInfo = static_cast(handler.GetInt()); + if (HandlerBase::IsField(handlerInfo)) { + StoreField(thread, JSObject::Cast(receiver.GetHeapObject()), value, handlerInfo); + return JSTaggedValue::Undefined(); + } + ASSERT(HandlerBase::IsAccessor(handlerInfo) || HandlerBase::IsInternalAccessor(handlerInfo)); + auto accessor = LoadFromField(JSObject::Cast(holder.GetHeapObject()), handlerInfo); + return FastRuntimeStub::CallSetter(thread, JSTaggedValue(receiver), value, accessor); + } + if (handler.IsTransitionHandler()) { + StoreWithTransition(thread, JSObject::Cast(receiver.GetHeapObject()), value, handler); + return JSTaggedValue::Undefined(); + } + if (handler.IsPrototypeHandler()) { + return StorePrototype(thread, receiver, value, handler); + } + if (handler.IsPropertyBox()) { + return StoreGlobal(thread, value, handler); + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ICRuntimeStub::StorePrototype(JSThread *thread, JSTaggedValue receiver, + JSTaggedValue value, JSTaggedValue handler) +{ + ASSERT(handler.IsPrototypeHandler()); + PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject()); + auto cellValue = prototypeHandler->GetProtoCell(); + ASSERT(cellValue.IsProtoChangeMarker()); + ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetHeapObject()); + if (cell->GetHasChanged()) { + return JSTaggedValue::Hole(); + } + auto holder = prototypeHandler->GetHolder(); + JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo(); + return StoreICWithHandler(thread, receiver, holder, value, handlerInfo); +} + +void ICRuntimeStub::StoreWithTransition(JSThread *thread, JSObject *receiver, JSTaggedValue value, + JSTaggedValue handler) +{ + TransitionHandler *transitionHandler = TransitionHandler::Cast(handler.GetTaggedObject()); + JSHClass *newHClass = JSHClass::Cast(transitionHandler->GetTransitionHClass().GetTaggedObject()); + receiver->SetClass(newHClass); + uint32_t handlerInfo = transitionHandler->GetHandlerInfo().GetInt(); + ASSERT(HandlerBase::IsField(handlerInfo)); + + if (!HandlerBase::IsInlinedProps(handlerInfo)) { + TaggedArray *array = TaggedArray::Cast(receiver->GetProperties().GetHeapObject()); + int capacity = array->GetLength(); + int index = HandlerBase::GetOffset(handlerInfo); + if (index >= capacity) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle properties; + JSHandle objHandle(thread, receiver); + if (capacity == 0) { + capacity = JSObject::MIN_PROPERTIES_LENGTH; + properties = factory->NewTaggedArray(capacity); + } else { + properties = factory->CopyArray(JSHandle(thread, array), capacity, + JSObject::ComputePropertyCapacity(capacity)); + } + properties->Set(thread, index, value); + objHandle->SetProperties(thread, properties); + return; + } + array->Set(thread, index, value); + return; + } + StoreField(thread, receiver, value, handlerInfo); +} + +void ICRuntimeStub::StoreField(JSThread *thread, JSObject *receiver, JSTaggedValue value, uint32_t handler) +{ + int index = HandlerBase::GetOffset(handler); + if (HandlerBase::IsInlinedProps(handler)) { + SET_VALUE_WITH_BARRIER(thread, receiver, index * JSTaggedValue::TaggedTypeSize(), value); + return; + } + TaggedArray *array = TaggedArray::Cast(receiver->GetProperties().GetHeapObject()); + ASSERT(index < static_cast(array->GetLength())); + array->Set(thread, index, value); +} + +JSTaggedValue ICRuntimeStub::LoadFromField(JSObject *receiver, uint32_t handlerInfo) +{ + int index = HandlerBase::GetOffset(handlerInfo); + if (HandlerBase::IsInlinedProps(handlerInfo)) { + return JSTaggedValue(GET_VALUE(receiver, index * JSTaggedValue::TaggedTypeSize())); + } + return TaggedArray::Cast(receiver->GetProperties().GetHeapObject())->Get(index); +} + +JSTaggedValue ICRuntimeStub::LoadGlobal(JSTaggedValue handler) +{ + ASSERT(handler.IsPropertyBox()); + PropertyBox *cell = PropertyBox::Cast(handler.GetHeapObject()); + if (cell->IsInvalid()) { + return JSTaggedValue::Hole(); + } + JSTaggedValue ret = cell->GetValue(); + ASSERT(!ret.IsAccessorData()); + return ret; +} + +JSTaggedValue ICRuntimeStub::StoreGlobal(JSThread *thread, JSTaggedValue value, JSTaggedValue handler) +{ + ASSERT(handler.IsPropertyBox()); + PropertyBox *cell = PropertyBox::Cast(handler.GetHeapObject()); + if (cell->IsInvalid()) { + return JSTaggedValue::Hole(); + } + ASSERT(!cell->GetValue().IsAccessorData()); + cell->SetValue(thread, value); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ICRuntimeStub::LoadPrototype(JSThread *thread, JSTaggedValue receiver, JSTaggedValue handler) +{ + ASSERT(handler.IsPrototypeHandler()); + PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject()); + auto cellValue = prototypeHandler->GetProtoCell(); + ASSERT(cellValue.IsProtoChangeMarker()); + ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetHeapObject()); + if (cell->GetHasChanged()) { + return JSTaggedValue::Hole(); + } + auto holder = prototypeHandler->GetHolder(); + JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo(); + return LoadICWithHandler(thread, receiver, holder, handlerInfo); +} + +JSTaggedValue ICRuntimeStub::LoadICWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, + JSTaggedValue handler) +{ + if (LIKELY(handler.IsInt())) { + auto handlerInfo = static_cast(handler.GetInt()); + if (LIKELY(HandlerBase::IsField(handlerInfo))) { + return LoadFromField(JSObject::Cast(holder.GetHeapObject()), handlerInfo); + } + if (HandlerBase::IsNonExist(handlerInfo)) { + return JSTaggedValue::Undefined(); + } + ASSERT(HandlerBase::IsAccessor(handlerInfo) || HandlerBase::IsInternalAccessor(handlerInfo)); + auto accessor = LoadFromField(JSObject::Cast(holder.GetHeapObject()), handlerInfo); + return FastRuntimeStub::CallGetter(thread, receiver, holder, accessor); + } + + if (handler.IsPrototypeHandler()) { + return LoadPrototype(thread, receiver, handler); + } + + return LoadGlobal(handler); +} + +JSTaggedValue ICRuntimeStub::LoadElement(JSObject *receiver, JSTaggedValue key) +{ + auto index = TryToElementsIndex(key); + if (index < 0) { + return JSTaggedValue::Hole(); + } + uint32_t elementIndex = index; + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetHeapObject()); + if (elements->GetLength() <= elementIndex) { + return JSTaggedValue::Hole(); + } + + JSTaggedValue value = elements->Get(elementIndex); + // TaggedArray + return value; +} + +JSTaggedValue ICRuntimeStub::StoreElement(JSThread *thread, JSObject *receiver, JSTaggedValue key, + JSTaggedValue value, uint32_t handlerInfo) +{ + ASSERT(HandlerBase::IsElement(handlerInfo)); + auto index = TryToElementsIndex(key); + if (index < 0) { + return JSTaggedValue::Hole(); + } + uint32_t elementIndex = index; + if (HandlerBase::IsJSArray(handlerInfo)) { + JSArray *arr = JSArray::Cast(receiver); + uint32_t oldLength = arr->GetArrayLength(); + if (elementIndex >= oldLength) { + arr->SetArrayLength(thread, elementIndex + 1); + } + } + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetHeapObject()); + uint32_t capacity = elements->GetLength(); + if (elementIndex >= capacity) { + if (JSObject::ShouldTransToDict(capacity, elementIndex)) { + return JSTaggedValue::Hole(); + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle receiverHandle(thread, receiver); + elements = *JSObject::GrowElementsCapacity(thread, receiverHandle, + JSObject::ComputeElementCapacity(elementIndex + 1)); + receiver->SetElements(thread, JSTaggedValue(elements)); + } + elements->Set(thread, elementIndex, value); + receiver->GetJSHClass()->UpdateRepresentation(value); + return JSTaggedValue::Undefined(); +} + +int32_t ICRuntimeStub::TryToElementsIndex(JSTaggedValue key) +{ + if (LIKELY(key.IsInt())) { + return key.GetInt(); + } + + if (key.IsString()) { + uint32_t index = 0; + if (JSTaggedValue::StringToElementIndex(key, &index)) { + return static_cast(index); + } + } + + if (key.IsDouble()) { + double number = key.GetDouble(); + auto integer = static_cast(number); + if (number == integer) { + return integer; + } + } + + return -1; +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_FAST_IC_RUNTIME_STUB_INL_H diff --git a/ecmascript/ic/fast_ic_runtime_stub.h b/ecmascript/ic/fast_ic_runtime_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..f61b84395c8ffdd0aaaf6fbbff631ab10ee1fa31 --- /dev/null +++ b/ecmascript/ic/fast_ic_runtime_stub.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_FAST_IC_RUNTIME_STUB_H +#define PANDA_RUNTIME_ECMASCRIPT_FAST_IC_RUNTIME_STUB_H + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/property_attributes.h" + +namespace panda::ecmascript { +class ICRuntimeStub { +public: + static inline JSTaggedValue LoadGlobalICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue globalValue, JSTaggedValue key, uint32_t slotId); + static inline JSTaggedValue StoreGlobalICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue globalValue, JSTaggedValue key, + JSTaggedValue value, uint32_t slotId); + static inline JSTaggedValue LoadICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue receiver, JSTaggedValue key, uint32_t slotId); + static inline JSTaggedValue StoreICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value, uint32_t slotId); + static inline JSTaggedValue CheckPolyHClass(JSTaggedValue cachedValue, JSHClass* hclass); + static inline JSTaggedValue LoadICWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, + JSTaggedValue handler); + static inline JSTaggedValue StoreICWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, + JSTaggedValue value, JSTaggedValue handler); + static inline void StoreWithTransition(JSThread *thread, JSObject *receiver, JSTaggedValue value, + JSTaggedValue handler); + static inline JSTaggedValue StorePrototype(JSThread *thread, JSTaggedValue receiver, + JSTaggedValue value, JSTaggedValue handler); + static inline JSTaggedValue LoadFromField(JSObject *receiver, uint32_t handlerInfo); + static inline void StoreField(JSThread *thread, JSObject *receiver, JSTaggedValue value, uint32_t handler); + static inline JSTaggedValue LoadGlobal(JSTaggedValue handler); + static inline JSTaggedValue StoreGlobal(JSThread *thread, JSTaggedValue value, JSTaggedValue handler); + static inline JSTaggedValue LoadPrototype(JSThread *thread, JSTaggedValue receiver, JSTaggedValue handler); + + static inline JSTaggedValue LoadICByValue(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue receiver, JSTaggedValue key, uint32_t slotId); + static inline JSTaggedValue StoreICByValue(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue receiver, JSTaggedValue key, JSTaggedValue value, + uint32_t slotId); + static inline JSTaggedValue LoadElement(JSObject *receiver, JSTaggedValue key); + static inline JSTaggedValue StoreElement(JSThread *thread, JSObject *receiver, JSTaggedValue key, + JSTaggedValue value, uint32_t handlerInfo); + static inline int32_t TryToElementsIndex(JSTaggedValue key); +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_FAST_IC_RUNTIME_STUB_H diff --git a/ecmascript/ic/function_cache.cpp b/ecmascript/ic/function_cache.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5bda4bd270bb8413e02011104d675297ec438c26 --- /dev/null +++ b/ecmascript/ic/function_cache.cpp @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ic/function_cache.h" +#include "ecmascript/ic/ic_handler-inl.h" +#include "ecmascript/js_function.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript { +JSTaggedValue FunctionCache::GetWeakRef(JSTaggedValue value) +{ + return JSTaggedValue(value.CreateAndGetWeakRef()); +} + +JSTaggedValue FunctionCache::GetRefFromWeak(const JSTaggedValue &value) +{ + return JSTaggedValue(value.GetTaggedWeakRef()); +} + +bool FunctionCache::AddHandler(const JSThread *thread, const JSHandle &cache, + const JSHandle &key, const JSHandle &dynclass, + const JSHandle &handler, uint16_t index) +{ + if (key->IsNull()) { + return AddHandlerWithoutKey(thread, cache, dynclass.GetTaggedValue(), handler.GetTaggedValue(), index); + } + return AddHandlerWithKey(thread, cache, key.GetTaggedValue(), dynclass.GetTaggedValue(), handler.GetTaggedValue(), + index); +} + +bool FunctionCache::AddHandlerWithoutKey(const JSThread *thread, const JSHandle &cache, + const JSTaggedValue &dynclass, const JSTaggedValue &handler, uint16_t index) +{ + ASSERT(index < MAX_FUNC_CACHE_INDEX - 1); + if (cache->Get(index) == JSTaggedValue::Undefined()) { + ASSERT(cache->Get(index + 1) == JSTaggedValue::Undefined()); + cache->Set(thread, index, cache->GetWeakRef(dynclass)); + cache->Set(thread, index + 1, handler); + return false; + } + if (cache->Get(index) == JSTaggedValue::Hole()) { + ASSERT(cache->Get(index + 1) == JSTaggedValue::Hole()); + return true; // for MEGA + } + JSTaggedValue cache_value = cache->Get(index); + if (!cache_value.IsWeak() && cache_value.IsTaggedArray()) { // POLY + ASSERT(cache->Get(index + 1) == JSTaggedValue::Hole()); + JSHandle arr(thread, cache_value); + const array_size_t STEP = 2; + array_size_t newLen = arr->GetLength() + STEP; + if (newLen > CACHE_MAX_LEN) { + return true; + } + JSHandle dynHandle(thread, dynclass); + JSHandle handlerHandle(thread, handler); + JSHandle newArr = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(newLen); + array_size_t i = 0; + for (; i < arr->GetLength(); i += STEP) { + newArr->Set(thread, i, arr->Get(i)); + newArr->Set(thread, i + 1, arr->Get(i + 1)); + } + ASSERT(i == (newLen - STEP)); + newArr->Set(thread, i, cache->GetWeakRef(dynHandle.GetTaggedValue())); + newArr->Set(thread, i + 1, handlerHandle.GetTaggedValue()); + cache->Set(thread, index, newArr.GetTaggedValue()); + return false; + } + // MONO to POLY + JSHandle dynHandle(thread, dynclass); + JSHandle handlerHandle(thread, handler); + JSHandle newArr = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(POLY_DEFAULT_LEN); + array_size_t arrIndex = 0; + newArr->Set(thread, arrIndex++, cache->Get(index)); + newArr->Set(thread, arrIndex++, cache->Get(index + 1)); + newArr->Set(thread, arrIndex++, cache->GetWeakRef(dynHandle.GetTaggedValue())); + newArr->Set(thread, arrIndex, handlerHandle.GetTaggedValue()); + + cache->Set(thread, index, newArr.GetTaggedValue()); + cache->Set(thread, index + 1, JSTaggedValue::Hole()); + return false; +} + +bool FunctionCache::AddHandlerWithKey(const JSThread *thread, const JSHandle &cache, + const JSTaggedValue &key, const JSTaggedValue &dynclass, + const JSTaggedValue &handler, uint16_t index) +{ + if (cache->Get(index) == JSTaggedValue::Undefined() && cache->Get(index + 1) == JSTaggedValue::Undefined()) { + cache->Set(thread, index, cache->GetWeakRef(key)); + JSHandle dynHandle(thread, dynclass); + JSHandle handlerHandle(thread, handler); + const int ARRAY_LENGTH = 2; + JSHandle newArr = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(ARRAY_LENGTH); + newArr->Set(thread, 0, cache->GetWeakRef(dynHandle.GetTaggedValue())); + newArr->Set(thread, 1, handlerHandle.GetTaggedValue()); + cache->Set(thread, index + 1, newArr.GetTaggedValue()); + return false; + } + if (cache->Get(index) == JSTaggedValue::Hole() && cache->Get(index + 1) == JSTaggedValue::Hole()) { + return true; // for MEGA + } + if (key != cache->GetRefFromWeak(cache->Get(index))) { + return false; + } + JSTaggedValue patchValue = cache->Get(index + 1); + ASSERT(patchValue.IsTaggedArray()); + JSHandle arr(thread, patchValue); + const array_size_t STEP = 2; + if (arr->GetLength() > STEP) { // POLY + array_size_t newLen = arr->GetLength() + STEP; + if (newLen > CACHE_MAX_LEN) { + return true; + } + JSHandle dynHandle(thread, dynclass); + JSHandle handlerHandle(thread, handler); + JSHandle newArr = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(newLen); + array_size_t i = 0; + for (; i < arr->GetLength(); i += STEP) { + newArr->Set(thread, i, arr->Get(i)); + newArr->Set(thread, i + 1, arr->Get(i + 1)); + } + ASSERT(i == (newLen - STEP)); + newArr->Set(thread, i, cache->GetWeakRef(dynHandle.GetTaggedValue())); + newArr->Set(thread, i + 1, handlerHandle.GetTaggedValue()); + cache->Set(thread, index + 1, newArr.GetTaggedValue()); + return false; + } + // MONO + JSHandle dynHandle(thread, dynclass); + JSHandle handlerHandle(thread, handler); + JSHandle newArr = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(POLY_DEFAULT_LEN); + array_size_t arrIndex = 0; + newArr->Set(thread, arrIndex++, arr->Get(0)); + newArr->Set(thread, arrIndex++, arr->Get(1)); + newArr->Set(thread, arrIndex++, cache->GetWeakRef(dynHandle.GetTaggedValue())); + newArr->Set(thread, arrIndex++, handlerHandle.GetTaggedValue()); + + cache->Set(thread, index + 1, newArr.GetTaggedValue()); + return false; +} + +void FunctionCache::TransToMega(const JSThread *thread, bool flag, const JSTaggedValue &key, + const JSTaggedValue &dynclass, const JSTaggedValue &handler, uint16_t index) +{ + Set(thread, index, JSTaggedValue::Hole()); + Set(thread, index + 1, JSTaggedValue::Hole()); +} + +JSTaggedValue FunctionCache::GetGlobalHandlerByValue(const JSTaggedValue &key, uint16_t index) +{ + JSTaggedValue indexVal = Get(index); + if (indexVal.IsTaggedArray()) { + TaggedArray *arr = TaggedArray::Cast(indexVal.GetTaggedObject()); + for (array_size_t i = 0; i < arr->GetLength(); i += 2) { // 2: skip a pair of key and value + if (GetRefFromWeak(arr->Get(i)) == key) { + return arr->Get(i + 1); + } + } + } + return JSTaggedValue::Null(); +} + +JSTaggedValue FunctionCache::GetGlobalHandlerByIndex(uint16_t index) +{ + JSTaggedValue indexVal = Get(index); + if (!indexVal.IsUndefined()) { + ASSERT(!indexVal.IsTaggedArray()); + return indexVal; + } + return JSTaggedValue::Null(); +} + +JSTaggedValue FunctionCache::GetHandlerByName(const JSTaggedValue &name, const JSTaggedValue &dynclass, uint16_t index) +{ + if (Get(index) == JSTaggedValue::Undefined() && Get(index + 1) == JSTaggedValue::Undefined()) { + return JSTaggedValue::Null(); + } + if (Get(index) == JSTaggedValue::Hole() && Get(index + 1) == JSTaggedValue::Hole()) { + return JSTaggedValue::Hole(); // MEGA return Hole to store/load inline cache + } + if (name != GetRefFromWeak(Get(index))) { + return JSTaggedValue::Null(); + } + JSTaggedValue patchValue = Get(index + 1); + ASSERT(patchValue.IsTaggedArray()); + TaggedArray *arr = TaggedArray::Cast(patchValue.GetTaggedObject()); + const array_size_t STEP = 2; + if (arr->GetLength() == STEP) { // MONO + if (GetRefFromWeak(arr->Get(0)) == dynclass) { + return arr->Get(1); + } + return JSTaggedValue::Null(); + } + if (arr->GetLength() > STEP) { // POLY + for (array_size_t i = 0; i < arr->GetLength(); i += STEP) { + if (GetRefFromWeak(arr->Get(i)) == dynclass) { + return arr->Get(i + 1); + } + } + return JSTaggedValue::Null(); + } + UNREACHABLE(); +} + +JSTaggedValue FunctionCache::GetHandlerByIndex(const JSTaggedValue &dynclass, uint16_t index) +{ + JSTaggedValue slot1 = Get(index); + if (slot1.IsWeak()) { + if (GetRefFromWeak(slot1) == dynclass) { + return Get(index + 1); + } + return JSTaggedValue::Null(); + } + if (slot1.IsUndefined()) { + return JSTaggedValue::Null(); + } + if (slot1.IsHole()) { + return JSTaggedValue::Hole(); // MEGA return Hole to store/load inline cache + } + ASSERT(slot1.IsTaggedArray()); // POLY + ASSERT(Get(index + 1) == JSTaggedValue::Hole()); + TaggedArray *arr = TaggedArray::Cast(slot1.GetTaggedObject()); + array_size_t length = arr->GetLength(); + const array_size_t STEP = 2; + for (array_size_t i = 0; i < length; i += STEP) { + if (GetRefFromWeak(arr->Get(i)) == dynclass) { + return arr->Get(i + 1); + } + } + return JSTaggedValue::Null(); +} + +JSHandle FunctionCache::Create(const JSThread *thread, uint16_t capacity) +{ + ASSERT(capacity > 0); + + auto length = static_cast(capacity); + JSHandle cache = thread->GetEcmaVM()->GetFactory()->NewFunctionCache(length); + return cache; +} + +JSTaggedValue FunctionCache::GetLoadHandler(JSThread *thread, JSTaggedValue key, const JSTaggedValue &dynclass, + uint16_t slotId) +{ + JSTaggedValue handler = GetHandlerByName(key, dynclass, slotId); + return handler; +} + +void FunctionCache::AddLoadHandler(JSThread *thread, const JSHandle &key, + const JSHandle &dynclass, const JSHandle &handler, + uint16_t slotId) +{ + JSHandle cache(thread, GetCurrent(thread)); + bool toMega = AddHandler(thread, cache, key, JSHandle(dynclass), handler, slotId); + if (toMega && !key->IsNull()) { + cache->TransToMega(thread, true, key.GetTaggedValue(), dynclass.GetTaggedValue(), handler.GetTaggedValue(), + slotId); + } +} + +void FunctionCache::AddGlobalHandler(JSThread *thread, const JSHandle &key, + const JSHandle &handler, uint16_t index) +{ + JSHandle cache(thread, GetCurrent(thread)); + if (key->IsNull()) { + return AddGlobalHandlerWithoutKey(thread, cache, handler, index); + } + return AddGlobalHandlerWithKey(thread, cache, key, handler, index); +} + +void FunctionCache::AddGlobalHandlerWithKey(JSThread *thread, const JSHandle &cache, + const JSHandle &key, const JSHandle &handler, + uint16_t index) +{ + const uint8_t pairSize = 2; // key and value pair + JSTaggedValue indexVal = cache->Get(index); + if (indexVal.IsUndefined()) { + JSHandle newArr = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(pairSize); + newArr->Set(thread, 0, cache->GetWeakRef(key.GetTaggedValue())); + newArr->Set(thread, 1, handler.GetTaggedValue()); + cache->Set(thread, index, newArr.GetTaggedValue()); + return; + } + ASSERT(indexVal.IsTaggedArray()); + JSHandle arr(thread, indexVal); + array_size_t newLen = arr->GetLength() + pairSize; + if (newLen > CACHE_MAX_LEN) { + return; + } + JSHandle newArr = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(newLen); + array_size_t i = 0; + for (; i < arr->GetLength(); i += pairSize) { + newArr->Set(thread, i, arr->Get(i)); + newArr->Set(thread, i + 1, arr->Get(i + 1)); + } + ASSERT(i == (newLen - pairSize)); + newArr->Set(thread, i, cache->GetWeakRef(key.GetTaggedValue())); + newArr->Set(thread, i + 1, handler.GetTaggedValue()); + cache->Set(thread, index, newArr.GetTaggedValue()); +} + +void FunctionCache::AddGlobalHandlerWithoutKey(JSThread *thread, const JSHandle &cache, + const JSHandle &handler, uint16_t index) +{ + cache->Set(thread, index, handler.GetTaggedValue()); +} + +JSTaggedValue FunctionCache::GetStoreHandler(JSThread *thread, JSTaggedValue key, const JSTaggedValue &dynclass, + uint16_t slotId) +{ + JSTaggedValue handler = GetHandlerByName(key, dynclass, slotId); + return handler; +} + +void FunctionCache::AddStoreHandler(JSThread *thread, const JSHandle &key, + const JSHandle &dynclass, const JSHandle &handler, + uint16_t slotId) +{ + JSHandle cache(thread, GetCurrent(thread)); + bool toMega = AddHandler(thread, cache, key, JSHandle(dynclass), handler, slotId); + if (toMega && !key->IsNull()) { + cache->TransToMega(thread, false, key.GetTaggedValue(), dynclass.GetTaggedValue(), handler.GetTaggedValue(), + slotId); + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/ic/function_cache.h b/ecmascript/ic/function_cache.h new file mode 100644 index 0000000000000000000000000000000000000000..b9b10c2de36285ac832845e605f9b5dce109abe0 --- /dev/null +++ b/ecmascript/ic/function_cache.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_FUNCTION_CACHE_H +#define PANDA_RUNTIME_FUNCTION_CACHE_H + +#include "ecmascript/js_function.h" +#include "ecmascript/tagged_array.h" +namespace panda { +namespace ecmascript { +class FunctionCache : public TaggedArray { +public: + static const array_size_t MAX_FUNC_CACHE_INDEX = std::numeric_limits::max(); + static constexpr uint16_t INVALID_SLOT_INDEX = 0xFF; + static constexpr array_size_t CACHE_MAX_LEN = 8; + static constexpr array_size_t POLY_DEFAULT_LEN = 4; + + static FunctionCache *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsTaggedArray()); + return static_cast(object); + } + + static inline FunctionCache *GetCurrent(JSThread *thread) + { + JSTaggedValue funcValue = EcmaFrameHandler(thread).GetFunction(); + JSFunction *func = JSFunction::Cast(funcValue.GetTaggedObject()); + return FunctionCache::Cast(func->GetFunctionCache().GetTaggedObject()); + } + + static JSHandle Create(const JSThread *thread, uint16_t capacity); + + JSTaggedValue GetWeakRef(JSTaggedValue value); + + JSTaggedValue GetRefFromWeak(const JSTaggedValue &value); + + // return true means trans to MEGA + static bool AddHandler(const JSThread *thread, const JSHandle &cache, + const JSHandle &key, const JSHandle &dynclass, + const JSHandle &handler, uint16_t index); + + static bool AddHandlerWithoutKey(const JSThread *thread, const JSHandle &cache, + const JSTaggedValue &dynclass, const JSTaggedValue &handler, uint16_t index); + + static bool AddHandlerWithKey(const JSThread *thread, const JSHandle &cache, + const JSTaggedValue &key, const JSTaggedValue &dynclass, const JSTaggedValue &handler, + uint16_t index); + + static void AddGlobalHandlerWithKey(JSThread *thread, const JSHandle &cache, + const JSHandle &key, const JSHandle &handler, + uint16_t index); + + static void AddGlobalHandlerWithoutKey(JSThread *thread, const JSHandle &cache, + const JSHandle &handler, uint16_t index); + + void TransToMega(const JSThread *thread, bool flag, const JSTaggedValue &key, const JSTaggedValue &dynclass, + const JSTaggedValue &handler, uint16_t index); // true for Load, false for store + + JSTaggedValue GetHandlerByName(const JSTaggedValue &name, const JSTaggedValue &dynclass, uint16_t index); + + JSTaggedValue GetHandlerByIndex(const JSTaggedValue &dynclass, uint16_t index); + + JSTaggedValue GetGlobalHandlerByValue(const JSTaggedValue &key, uint16_t index); + + JSTaggedValue GetGlobalHandlerByIndex(uint16_t index); + + JSTaggedValue GetLoadHandler(JSThread *thread, JSTaggedValue key, const JSTaggedValue &dynclass, uint16_t slotId); + + JSTaggedValue GetStoreHandler(JSThread *thread, JSTaggedValue key, const JSTaggedValue &dynclass, uint16_t slotId); + + static void AddLoadHandler(JSThread *thread, const JSHandle &key, const JSHandle &dynclass, + const JSHandle &handler, uint16_t slotId); + + static void AddGlobalHandler(JSThread *thread, const JSHandle &key, + const JSHandle &handler, uint16_t index); + + static void AddStoreHandler(JSThread *thread, const JSHandle &key, + const JSHandle &dynclass, const JSHandle &handler, + uint16_t slotId); +}; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_FUNCTION_CACHE_H diff --git a/ecmascript/ic/ic_accessor-inl.h b/ecmascript/ic/ic_accessor-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..2a52fc214c9369094f1683c975261afbd9f8b5f3 --- /dev/null +++ b/ecmascript/ic/ic_accessor-inl.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_IC_ACCESSOR_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_IC_ACCESSOR_INL_H + +#include "ecmascript/accessor_data.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_proxy.h" +#include "ecmascript/object_factory.h" +#include "function_cache.h" +#include "ic_accessor.h" +#include "ic_handler-inl.h" + +namespace panda::ecmascript { +inline JSTaggedValue ICAccessor::LoadGlobalWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue handler) +{ + if (handler.IsPrototypeHandler()) { + return LoadGlobalPrototype(thread, receiver, handler); + } + if (handler.IsGlobalHandler()) { + return LoadGlobal(thread, receiver, handler); + } + ASSERT(handler.IsNonExistentHandler()); + return LoadNonExistent(handler); +} + +inline JSTaggedValue ICAccessor::LoadGlobalPrototype(JSThread *thread, JSTaggedValue obj, JSTaggedValue handler) +{ + ASSERT(handler.IsPrototypeHandler()); + PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject()); + if (JSHClass::HasProtoChainChanged(prototypeHandler->GetProtoCell())) { + return JSTaggedValue::Hole(); + } + JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo(); + ASSERT(handlerInfo.IsGlobalHandler()); + return LoadGlobal(thread, obj, handlerInfo); +} + +inline JSTaggedValue ICAccessor::LoadNonExistent(JSTaggedValue handler) +{ + NonExistentHandler *nonExistentHandler = NonExistentHandler::Cast(handler.GetTaggedObject()); + if (JSHClass::HasProtoChainChanged(nonExistentHandler->GetProtoCell())) { + return JSTaggedValue::Hole(); + } + return JSTaggedValue::Undefined(); +} + +inline bool ICAccessor::StoreGlobalICByName(JSThread *thread, uint32_t stringId, JSTaggedValue value, uint16_t slotId) +{ + FunctionCache *cache = FunctionCache::GetCurrent(thread); + JSTaggedValue handler = cache->GetGlobalHandlerByIndex(slotId); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + bool success = false; + if (!handler.IsNull()) { + JSTaggedValue globalValue = thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject(); + success = StoreGlobalWithHandlerByName(thread, globalValue, stringId, value, handler, slotId); + } else { + JSTaggedValue key(factory->ResolveString(stringId)); + success = StoreGlobalMissByName(thread, JSHandle(thread, key), + JSHandle(thread, value), slotId); + } + return success; +} + +inline bool ICAccessor::StoreGlobalWithHandlerByName(JSThread *thread, JSTaggedValue obj, uint32_t stringId, + JSTaggedValue value, JSTaggedValue handler, uint16_t slotId) +{ + if (handler.IsPrototypeHandler()) { + return StoreGlobalPrototypeByName(thread, obj, stringId, value, handler, slotId); + } + if (handler.IsGlobalHandler()) { + return StoreGlobalByName(thread, JSObject::Cast(obj.GetTaggedObject()), stringId, value, handler, slotId); + } + ASSERT(handler.IsTransitionHandler()); + StoreWithTransition(thread, JSObject::Cast(obj.GetTaggedObject()), value, handler); + return true; +} + +inline bool ICAccessor::StoreGlobalWithHandlerByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, + JSTaggedValue value, JSTaggedValue handler, uint16_t slotId) +{ + if (handler.IsPrototypeHandler()) { + return StoreGlobalPrototypeByValue(thread, obj, key, value, handler, slotId); + } + if (handler.IsGlobalHandler()) { + return StoreGlobalByValue(thread, JSObject::Cast(obj.GetTaggedObject()), key, value, handler, slotId); + } + ASSERT(handler.IsTransitionHandler()); + StoreWithTransition(thread, JSObject::Cast(obj.GetTaggedObject()), value, handler); + return true; +} + +inline bool ICAccessor::StoreGlobalPrototypeByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, + JSTaggedValue value, JSTaggedValue handler, uint16_t slotId) +{ + ASSERT(handler.IsPrototypeHandler()); + PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject()); + if (JSHClass::HasProtoChainChanged(prototypeHandler->GetProtoCell())) { + JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo(); + ASSERT(handlerInfo.IsGlobalHandler()); + GlobalHandler *globalHandler = GlobalHandler::Cast(handlerInfo.GetTaggedObject()); + PropertyBox *cell = PropertyBox::Cast(globalHandler->GetPropertyBox().GetTaggedObject()); + auto *setter = AccessorData::Cast(cell->GetValue().GetTaggedObject()); + JSObject::CallSetter(thread, *setter, JSHandle(thread, obj), + JSHandle(thread, value)); + return true; + } + JSHandle value_handle(thread, value); + return StoreGlobalMissByValue(thread, JSHandle(thread, key), JSHandle(thread, value), + slotId); +} +inline void ICAccessor::StoreWithTransition(const JSThread *thread, JSObject *obj, JSTaggedValue value, + JSTaggedValue handler) +{ + TransitionHandler *transitionHandler = TransitionHandler::Cast(handler.GetTaggedObject()); + JSHClass *newDynclass = JSHClass::Cast(transitionHandler->GetTransitionHClass().GetTaggedObject()); + obj->SetClass(newDynclass); + uint32_t handlerInfo = transitionHandler->GetHandlerInfo().GetInt(); + ASSERT(HandlerBase::IsField(handlerInfo)); + StoreField(thread, obj, value, handlerInfo); +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_IC_ACCESSOR_INL_H diff --git a/ecmascript/ic/ic_accessor.cpp b/ecmascript/ic/ic_accessor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..593929b7c0a8e16dd8f4d70cfc673d8460a228e2 --- /dev/null +++ b/ecmascript/ic/ic_accessor.cpp @@ -0,0 +1,790 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ic_accessor.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/mem/assert_scope-inl.h" +#include "ic_accessor-inl.h" + +namespace panda::ecmascript { +JSTaggedValue ICAccessor::LoadMiss(JSThread *thread, const JSHandle &obj, const JSHandle &key, + uint16_t slotId) +{ + if (obj->IsTypedArray()) { + return JSTaggedValue::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue(); + } + ObjectOperator op(thread, obj, key); + // ic-switch + if (!thread->GetEcmaVM()->ICEnable()) { + return JSObject::GetProperty(thread, &op); + } + // do not cache element + if (!op.IsFastMode() || op.IsElement()) { + return JSObject::GetProperty(thread, &op); + } + JSTaggedValue handler; + JSHandle dynclass(thread, obj->GetJSHClass()); + if (!op.IsFound()) { + handler = NonExistentHandler::LoadNonExistent(thread, dynclass); + } else if (!op.IsOnPrototype()) { + handler = LoadHandler::LoadProperty(thread, op); + } else { + handler = PrototypeHandler::LoadPrototype(thread, op, dynclass); + } + // add handler to ic slot + FunctionCache::AddLoadHandler(thread, key, dynclass, JSHandle(thread, handler), slotId); + return JSObject::GetProperty(thread, &op); +} + +JSTaggedValue ICAccessor::LoadMissByName(JSThread *thread, const JSHandle &obj, + const JSHandle &key, uint16_t slotId) +{ + if (obj->IsTypedArray()) { + return JSTaggedValue::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue(); + } + ObjectOperator op(thread, obj, key); + // ic-switch + if (!thread->GetEcmaVM()->ICEnable()) { + return JSObject::GetProperty(thread, &op); + } + // do not cache element + if (!op.IsFastMode() || op.IsElement()) { + return JSObject::GetProperty(thread, &op); + } + JSTaggedValue handler; + JSHandle dynclass(thread, obj->GetJSHClass()); + if (!op.IsFound()) { + handler = NonExistentHandler::LoadNonExistent(thread, dynclass); + } else if (!op.IsOnPrototype()) { + handler = LoadHandler::LoadProperty(thread, op); + } else { + handler = PrototypeHandler::LoadPrototype(thread, op, dynclass); + } + // add handler to ic slot + FunctionCache::AddLoadHandler(thread, JSHandle(thread, JSTaggedValue::Null()), dynclass, + JSHandle(thread, handler), slotId); + return JSObject::GetProperty(thread, &op); +} + +bool ICAccessor::StoreMiss(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, uint16_t slotId) +{ + if (obj->IsTypedArray()) { + return JSTaggedValue::SetProperty(thread, JSHandle(obj), key, value); + } + ObjectOperator op(thread, obj, key); + bool success = JSObject::SetProperty(&op, value, false); + if (!thread->GetEcmaVM()->ICEnable()) { + return success; + } + + if (!success) { + return false; + } + // do not cache element and proxy + if (!op.IsFastMode() || op.IsElement() || op.GetHolder()->IsJSProxy()) { + return true; + } + // if !op.IsElement && !op.IsFound, target will be added in function AddPropertyInternal(), at the same time op will + // be set found() + ASSERT(op.IsFound()); + JSHandle dynclass(thread, obj->GetJSHClass()); + JSTaggedValue handler; + if (op.IsOnPrototype()) { + // if SetProperty successfully, op.IsOnPrototype will be reset if op.IsAccessorDescriptor is false; + ASSERT(op.IsAccessorDescriptor()); + handler = PrototypeHandler::StorePrototype(thread, op, dynclass); + } else if (*dynclass == obj->GetJSHClass()) { + handler = StoreHandler::StoreProperty(thread, op); + } else { + handler = TransitionHandler::StoreTransition(thread, op); + } + // add handler to ic slot + FunctionCache::AddStoreHandler(thread, key, dynclass, JSHandle(thread, handler), slotId); + return true; +} + +bool ICAccessor::StoreMissByName(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, uint16_t slotId) +{ + if (obj->IsTypedArray()) { + return JSTaggedValue::SetProperty(thread, JSHandle(obj), key, value); + } + ObjectOperator op(thread, obj, key); + bool success = JSObject::SetProperty(&op, value, false); + if (!thread->GetEcmaVM()->ICEnable()) { + return success; + } + + if (!success) { + return false; + } + // do not cache element and proxy + if (!op.IsFastMode() || op.IsElement() || op.GetHolder()->IsJSProxy()) { + return true; + } + // if !op.IsElement && !op.IsFound, target will be added in function AddPropertyInternal(), at the same time op will + // be set found() + ASSERT(op.IsFound()); + JSHandle dynclass(thread, obj->GetJSHClass()); + JSTaggedValue handler; + if (op.IsOnPrototype()) { + // if SetProperty successfully, op.IsOnPrototype will be reset if op.IsAccessorDescriptor is false; + ASSERT(op.IsAccessorDescriptor()); + handler = PrototypeHandler::StorePrototype(thread, op, dynclass); + } else if (*dynclass == obj->GetJSHClass()) { + handler = StoreHandler::StoreProperty(thread, op); + } else { + handler = TransitionHandler::StoreTransition(thread, op); + } + // add handler to ic slot + FunctionCache::AddStoreHandler(thread, JSHandle(thread, JSTaggedValue::Null()), dynclass, + JSHandle(thread, handler), slotId); + return true; +} + +JSTaggedValue ICAccessor::LoadGlobalMiss(JSThread *thread, const JSHandle &key, uint16_t slotId) +{ + ObjectOperator op(thread, key); + // ic-switch + if (!thread->GetEcmaVM()->ICEnable()) { + return JSObject::GetProperty(thread, &op); + } + // do not cache element + if (op.IsElement()) { + return JSObject::GetProperty(thread, &op); + } + JSTaggedValue handler; + if (!op.IsFound()) { + handler = NonExistentHandler::LoadGlobalNonExistent(thread); + } else if (!op.IsOnPrototype()) { + handler = LoadHandler::LoadGlobalProperty(thread, op); + } else { + handler = PrototypeHandler::LoadGlobalPrototype(thread, op); + } + // add handler to ic slot + FunctionCache::AddGlobalHandler(thread, key, JSHandle(thread, handler), slotId); + return JSObject::GetProperty(thread, &op); +} + +JSTaggedValue ICAccessor::LoadGlobalMissByName(JSThread *thread, const JSHandle &key, uint16_t slotId) +{ + ObjectOperator op(thread, key); + // ic-switch + if (!thread->GetEcmaVM()->ICEnable()) { + return JSObject::GetProperty(thread, &op); + } + // do not cache element + if (op.IsElement()) { + return JSObject::GetProperty(thread, &op); + } + JSTaggedValue handler; + if (!op.IsFound()) { + handler = NonExistentHandler::LoadGlobalNonExistent(thread); + } else if (!op.IsOnPrototype()) { + handler = LoadHandler::LoadGlobalProperty(thread, op); + } else { + handler = PrototypeHandler::LoadGlobalPrototype(thread, op); + } + // add handler to ic slot + FunctionCache::AddGlobalHandler(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, handler), slotId); + return JSObject::GetProperty(thread, &op); +} + +bool ICAccessor::StoreGlobalMissByName(JSThread *thread, const JSHandle &key, + const JSHandle &value, uint16_t slotId) +{ + ObjectOperator op(thread, key); + bool success = JSObject::SetProperty(&op, value, false); + if (!thread->GetEcmaVM()->ICEnable()) { + return success; + } + + if (!success) { + return false; + } + // do not cache element and proxy + if (op.IsElement() || op.GetHolder()->IsJSProxy()) { + return true; + } + ASSERT(op.IsFound()); + JSTaggedValue handler; + if (op.IsOnPrototype()) { + // if SetProperty successfully, op.IsOnPrototype will be reset if op.IsAccessorDescriptor is false; + ASSERT(op.IsAccessorDescriptor()); + handler = PrototypeHandler::StoreGlobalPrototype(thread, op); + FunctionCache::AddGlobalHandler(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, handler), slotId); + return true; + } + JSHandle obj(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()); + JSHandle dynclass(thread, obj->GetJSHClass()); + if (*dynclass == obj->GetJSHClass()) { + handler = StoreHandler::StoreGlobalProperty(thread, op); + } else { + handler = TransitionHandler::StoreTransition(thread, op); + } + // add handler to ic slot + FunctionCache::AddGlobalHandler(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, handler), slotId); + return true; +} + +bool ICAccessor::StoreGlobalMissByValue(JSThread *thread, const JSHandle &key, + const JSHandle &value, uint16_t slotId) +{ + ObjectOperator op(thread, key); + bool success = JSObject::SetProperty(&op, value, false); + if (!thread->GetEcmaVM()->ICEnable()) { + return success; + } + + if (!success) { + return false; + } + // do not cache element and proxy + if (op.IsElement() || op.GetHolder()->IsJSProxy()) { + return true; + } + ASSERT(op.IsFound()); + JSTaggedValue handler; + if (op.IsOnPrototype()) { + // if SetProperty successfully, op.IsOnPrototype will be reset if op.IsAccessorDescriptor is false; + ASSERT(op.IsAccessorDescriptor()); + handler = PrototypeHandler::StoreGlobalPrototype(thread, op); + FunctionCache::AddGlobalHandler(thread, key, JSHandle(thread, handler), slotId); + return true; + } + JSHandle obj(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()); + JSHandle dynclass(thread, obj->GetJSHClass()); + if (*dynclass == obj->GetJSHClass()) { + handler = StoreHandler::StoreGlobalProperty(thread, op); + } else { + handler = TransitionHandler::StoreTransition(thread, op); + } + // add handler to ic slot + FunctionCache::AddGlobalHandler(thread, key, JSHandle(thread, handler), slotId); + return true; +} + +JSTaggedValue ICAccessor::LoadIC(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, uint16_t slotId) +{ + if (receiver.IsUndefined() || receiver.IsNull() || receiver.IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "LoadIC--receiver is not valid", JSTaggedValue::Exception()); + } + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(JSHandle(thread, key)), "Key is not a property key"); + if (!receiver.IsECMAObject()) { + return JSTaggedValue::GetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key)) + .GetValue() + .GetTaggedValue(); + } + if (key.IsNumber()) { + if (!receiver.IsJSPrimitiveRef() && !receiver.IsTypedArray()) { + JSTaggedValue val = FastRuntimeStub::FastGetPropertyByIndex(thread, receiver, key.GetArrayLength()); + if (UNLIKELY(val.IsAccessorData())) { + return JSObject::CallGetter(thread, AccessorData::Cast(val.GetTaggedObject()), + JSHandle(thread, receiver)); + } + return val; + } + return JSTaggedValue::GetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key)) + .GetValue() + .GetTaggedValue(); + } + JSObject *obj = JSObject::Cast(receiver.GetTaggedObject()); + JSTaggedValue dynclass(obj->GetJSHClass()); + FunctionCache *cache = FunctionCache::GetCurrent(thread); + JSTaggedValue handler = cache->GetLoadHandler(thread, key, dynclass, slotId); + if (!handler.IsNull()) { + JSTaggedValue ret = LoadWithHandler(thread, obj, receiver, handler); + if (!ret.IsHole()) { + return ret; + } + } + return LoadMiss(thread, JSHandle(thread, obj), JSHandle(thread, key), slotId); +} + +JSTaggedValue ICAccessor::LoadICByName(JSThread *thread, JSTaggedValue receiver, uint32_t stringId, uint16_t slotId) +{ + if (receiver.IsUndefined() || receiver.IsNull() || receiver.IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "LoadIC--receiver is not valid", JSTaggedValue::Exception()); + } + if (UNLIKELY(!receiver.IsECMAObject())) { + JSHandle receiverHandle(thread, receiver); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSTaggedValue key(factory->ResolveString(stringId)); + return JSTaggedValue::GetProperty(thread, receiverHandle, JSHandle(thread, key)) + .GetValue() + .GetTaggedValue(); + } + JSObject *obj = JSObject::Cast(receiver.GetTaggedObject()); + JSTaggedValue dynclass(obj->GetJSHClass()); + FunctionCache *cache = FunctionCache::GetCurrent(thread); + JSTaggedValue handler = cache->GetHandlerByIndex(dynclass, slotId); + if (!handler.IsNull()) { + JSTaggedValue ret = LoadWithHandler(thread, obj, receiver, handler); + if (!ret.IsHole()) { + return ret; + } + } + JSHandle objHandle(thread, obj); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSTaggedValue key(factory->ResolveString(stringId)); + return LoadMissByName(thread, objHandle, JSHandle(thread, key), slotId); +} + +// dispatch +JSTaggedValue ICAccessor::LoadWithHandler(JSThread *thread, JSObject *obj, JSTaggedValue receiver, + JSTaggedValue handler) +{ + if (handler.IsInt()) { + return LoadProperty(thread, obj, receiver, handler); + } + if (handler.IsPrototypeHandler()) { + return LoadPrototype(thread, obj, handler); + } + if (handler.IsGlobalHandler()) { + return LoadGlobal(thread, receiver, handler); + } + ASSERT(handler.IsNonExistentHandler()); + return LoadNonExistent(handler); +} + +JSTaggedValue ICAccessor::LoadProperty(JSThread *thread, JSObject *obj, JSTaggedValue receiver, JSTaggedValue handler) +{ + ASSERT(handler.IsInt()); + int32_t handlerInfo = handler.GetInt(); + JSTaggedValue ret; + + if (HandlerBase::IsInlinedProps(handlerInfo)) { + int index = HandlerBase::GetOffset(handlerInfo); + ret = obj->GetPropertyInlinedProps(index); + } else if (HandlerBase::IsNonInlinedProps(handlerInfo)) { + int index = HandlerBase::GetOffset(handlerInfo); + ret = TaggedArray::Cast(obj->GetProperties().GetTaggedObject())->Get(index); + } else { + UNREACHABLE(); + } + if (HandlerBase::IsField(handlerInfo)) { + return ret; + } + + if (HandlerBase::IsAccessor(handlerInfo)) { + return JSObject::CallGetter(thread, AccessorData::Cast(ret.GetTaggedObject()), + JSHandle(thread, receiver)); + } + ASSERT(HandlerBase::IsInternalAccessor(handlerInfo)); + return AccessorData::Cast(ret.GetTaggedObject())->CallInternalGet(thread, JSHandle(thread, receiver)); +} + +JSTaggedValue ICAccessor::LoadGlobal(JSThread *thread, JSTaggedValue receiver, JSTaggedValue handler) +{ + ASSERT(handler.IsGlobalHandler()); + GlobalHandler *globalHandler = GlobalHandler::Cast(handler.GetTaggedObject()); + PropertyBox *cell = PropertyBox::Cast(globalHandler->GetPropertyBox().GetTaggedObject()); + if (cell->IsInvalid()) { + return JSTaggedValue::Hole(); + } + JSTaggedValue ret = cell->GetValue(); + if (ret.IsAccessorData()) { + return JSObject::CallGetter(thread, AccessorData::Cast(ret.GetTaggedObject()), + JSHandle(thread, receiver)); + } + return ret; +} + +JSTaggedValue ICAccessor::LoadPrototype(JSThread *thread, JSObject *obj, JSTaggedValue handler) +{ + ASSERT(handler.IsPrototypeHandler()); + PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject()); + if (JSHClass::HasProtoChainChanged(prototypeHandler->GetProtoCell())) { + return JSTaggedValue::Hole(); + } + JSObject *holder = JSObject::Cast(prototypeHandler->GetHolder().GetTaggedObject()); + JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo(); + ASSERT(handlerInfo.IsInt() || handlerInfo.IsGlobalHandler()); + if (handlerInfo.IsInt()) { + return LoadProperty(thread, holder, JSTaggedValue(obj), handlerInfo); + } + if (handlerInfo.IsGlobalHandler()) { + return LoadGlobal(thread, JSTaggedValue(obj), handlerInfo); + } + UNREACHABLE(); +} + +bool ICAccessor::StoreIC(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, JSTaggedValue value, + uint16_t slotId) +{ + if (receiver.IsUndefined() || receiver.IsNull() || receiver.IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "StoreIC--receiver is not valid", false); + } + if (!receiver.IsECMAObject()) { + return JSTaggedValue::SetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key), JSHandle(thread, value)); + } + + if (key.IsNumber()) { + if (!receiver.IsArray(thread) && !receiver.IsTypedArray()) { + return FastRuntimeStub::FastSetPropertyByIndex(thread, receiver, key.GetArrayLength(), value); + } + return JSTaggedValue::SetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key), JSHandle(thread, value)); + } + JSObject *obj = JSObject::Cast(receiver.GetTaggedObject()); + JSTaggedValue dynclass(obj->GetJSHClass()); + FunctionCache *cache = FunctionCache::GetCurrent(thread); + JSTaggedValue handler = cache->GetStoreHandler(thread, key, dynclass, slotId); + bool success = false; + if (!handler.IsNull()) { + success = StoreWithHandler(thread, obj, key, value, handler, slotId); + } else { + JSHandle prop = JSTaggedValue::ToPropertyKey(thread, JSHandle(thread, key)); + success = + StoreMiss(thread, JSHandle(thread, obj), prop, JSHandle(thread, value), slotId); + } + return success; +} + +bool ICAccessor::StoreICByName(JSThread *thread, JSTaggedValue receiver, uint32_t stringId, JSTaggedValue value, + uint16_t slotId) +{ + if (receiver.IsUndefined() || receiver.IsNull() || receiver.IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "StoreIC--receiver is not valid", false); + } + if (!receiver.IsECMAObject()) { + JSHandle receiverHandle(thread, receiver); + JSHandle valueHandle(thread, value); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSTaggedValue key(factory->ResolveString(stringId)); + return JSTaggedValue::SetProperty(thread, receiverHandle, JSHandle(thread, key), valueHandle); + } + JSObject *obj = JSObject::Cast(receiver.GetTaggedObject()); + JSTaggedValue dynclass(obj->GetJSHClass()); + FunctionCache *cache = FunctionCache::GetCurrent(thread); + JSTaggedValue handler = cache->GetHandlerByIndex(dynclass, slotId); + bool success = false; + if (!handler.IsNull()) { + success = StoreWithHandlerByName(thread, obj, stringId, value, handler, slotId); + } else { + JSHandle objHandle(thread, obj); + JSHandle valueHandle(thread, value); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSTaggedValue key(factory->ResolveString(stringId)); + success = StoreMissByName(thread, objHandle, JSHandle(thread, key), valueHandle, slotId); + } + return success; +} + +bool ICAccessor::StoreWithHandler(JSThread *thread, JSObject *obj, JSTaggedValue key, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId) +{ + if (handler.IsInt()) { + StoreProperty(thread, obj, value, handler.GetInt()); + return true; + } + if (handler.IsGlobalHandler()) { + return StoreGlobal(thread, obj, key, value, handler, slotId); + } + if (handler.IsTransitionHandler()) { + StoreWithTransition(thread, obj, value, handler); + return true; + } + if (handler.IsPrototypeHandler()) { + return StorePrototype(thread, obj, key, value, handler, slotId); + } + UNREACHABLE(); +} + +bool ICAccessor::StoreWithHandlerByName(JSThread *thread, JSObject *obj, uint32_t stringId, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId) +{ + if (handler.IsInt()) { + StoreProperty(thread, obj, value, handler.GetInt()); + return true; + } + if (handler.IsGlobalHandler()) { + return StoreGlobalByName(thread, obj, stringId, value, handler, slotId); + } + if (handler.IsTransitionHandler()) { + StoreWithTransition(thread, obj, value, handler); + return true; + } + if (handler.IsPrototypeHandler()) { + return StorePrototypeByName(thread, obj, stringId, value, handler, slotId); + } + UNREACHABLE(); +} + +void ICAccessor::StoreProperty(JSThread *thread, JSObject *obj, JSTaggedValue value, uint32_t handler) +{ + if (StoreHandler::IsField(handler)) { + StoreField(thread, obj, value, handler); + } else if (StoreHandler::IsAccessor(handler)) { + auto *setter = GetAccessor(thread, obj, handler); + JSObject::CallSetter(thread, *setter, JSHandle(thread, obj), + JSHandle(thread, value)); + } else { + ASSERT(HandlerBase::IsInternalAccessor(handler)); + AccessorData *accessor = GetAccessor(thread, obj, handler); + accessor->CallInternalSet(thread, JSHandle(thread, obj), JSHandle(thread, value)); + } +} + +void ICAccessor::StoreField(const JSThread *thread, JSObject *obj, JSTaggedValue value, uint32_t handler) +{ + int index = HandlerBase::GetOffset(handler); + if (HandlerBase::IsInlinedProps(handler)) { + obj->SetPropertyInlinedProps(thread, index, value); + return; + } + ASSERT(HandlerBase::IsNonInlinedProps(handler)); + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + int capacity = array->GetLength(); + // grow capacity + if (UNLIKELY(index >= capacity)) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle properties; + JSHandle objHandle(thread, obj); + if (capacity == 0) { + capacity = JSObject::MIN_PROPERTIES_LENGTH; + properties = factory->NewTaggedArray(capacity); + } else { + properties = factory->CopyArray(JSHandle(thread, array), capacity, + JSObject::ComputePropertyCapacity(capacity)); + } + properties->Set(thread, index, value); + objHandle->SetProperties(thread, properties); + return; + } + + array->Set(thread, index, value); +} + +bool ICAccessor::StoreGlobal(JSThread *thread, JSObject *obj, JSTaggedValue key, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId) +{ + ASSERT(handler.IsGlobalHandler()); + GlobalHandler *globalHandler = GlobalHandler::Cast(handler.GetTaggedObject()); + PropertyBox *cell = PropertyBox::Cast(globalHandler->GetPropertyBox().GetTaggedObject()); + JSTaggedValue val = cell->GetValue(); + if (cell->IsInvalid()) { + return StoreMiss(thread, JSHandle(thread, obj), JSHandle(thread, key), + JSHandle(thread, value), slotId); + } + if (UNLIKELY(val.IsAccessorData())) { + auto *setter = AccessorData::Cast(val.GetTaggedObject()); + JSObject::CallSetter(thread, *setter, JSHandle(thread, obj), + JSHandle(thread, value)); + return true; + } + cell->SetValue(thread, value); + return true; +} + +bool ICAccessor::StoreGlobalByName(JSThread *thread, JSObject *obj, uint32_t stringId, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId) +{ + ASSERT(handler.IsGlobalHandler()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + GlobalHandler *globalHandler = GlobalHandler::Cast(handler.GetTaggedObject()); + PropertyBox *cell = PropertyBox::Cast(globalHandler->GetPropertyBox().GetTaggedObject()); + JSTaggedValue val = cell->GetValue(); + if (cell->IsInvalid()) { + JSHandle valueHandle(thread, value); + JSTaggedValue key(factory->ResolveString(stringId)); + return StoreGlobalMissByName(thread, JSHandle(thread, key), valueHandle, slotId); + } + if (UNLIKELY(val.IsAccessorData())) { + auto *setter = AccessorData::Cast(val.GetTaggedObject()); + JSObject::CallSetter(thread, *setter, JSHandle(thread, obj), + JSHandle(thread, value)); + return true; + } + cell->SetValue(thread, value); + return true; +} + +bool ICAccessor::StoreGlobalByValue(JSThread *thread, JSObject *obj, JSTaggedValue key, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId) +{ + ASSERT(handler.IsGlobalHandler()); + GlobalHandler *globalHandler = GlobalHandler::Cast(handler.GetTaggedObject()); + PropertyBox *cell = PropertyBox::Cast(globalHandler->GetPropertyBox().GetTaggedObject()); + JSTaggedValue val = cell->GetValue(); + if (cell->IsInvalid()) { + JSHandle valueHandle(thread, value); + JSHandle keyHandle(thread, key); + return StoreGlobalMissByValue(thread, keyHandle, valueHandle, slotId); + } + if (UNLIKELY(val.IsAccessorData())) { + auto *setter = AccessorData::Cast(val.GetTaggedObject()); + JSObject::CallSetter(thread, *setter, JSHandle(thread, obj), + JSHandle(thread, value)); + return true; + } + cell->SetValue(thread, value); + return true; +} + +bool ICAccessor::StorePrototype(JSThread *thread, JSObject *obj, JSTaggedValue key, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId) +{ + ASSERT(handler.IsPrototypeHandler()); + PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject()); + if (JSHClass::HasProtoChainChanged(prototypeHandler->GetProtoCell())) { + JSObject *holder = JSObject::Cast(prototypeHandler->GetHolder().GetTaggedObject()); + JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo(); + AccessorData *setter = nullptr; + if (handlerInfo.IsInt()) { + setter = GetAccessor(thread, holder, handlerInfo.GetInt()); + } else { + ASSERT(handlerInfo.IsGlobalHandler()); + GlobalHandler *globalHandler = GlobalHandler::Cast(handlerInfo.GetTaggedObject()); + PropertyBox *cell = PropertyBox::Cast(globalHandler->GetPropertyBox().GetTaggedObject()); + setter = AccessorData::Cast(cell->GetValue().GetTaggedObject()); + } + JSObject::CallSetter(thread, *setter, JSHandle(thread, obj), + JSHandle(thread, value)); + return true; + } + return StoreMiss(thread, JSHandle(thread, obj), JSHandle(thread, key), + JSHandle(thread, value), slotId); +} + +bool ICAccessor::StorePrototypeByName(JSThread *thread, JSObject *obj, uint32_t stringId, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId) +{ + ASSERT(handler.IsPrototypeHandler()); + PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject()); + if (JSHClass::HasProtoChainChanged(prototypeHandler->GetProtoCell())) { + JSObject *holder = JSObject::Cast(prototypeHandler->GetHolder().GetTaggedObject()); + JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo(); + AccessorData *setter; + if (handlerInfo.IsInt()) { + setter = GetAccessor(thread, holder, handlerInfo.GetInt()); + } else { + ASSERT(handlerInfo.IsGlobalHandler()); + GlobalHandler *globalHandler = GlobalHandler::Cast(handlerInfo.GetTaggedObject()); + PropertyBox *cell = PropertyBox::Cast(globalHandler->GetPropertyBox().GetTaggedObject()); + setter = AccessorData::Cast(cell->GetValue().GetTaggedObject()); + } + JSObject::CallSetter(thread, *setter, JSHandle(thread, obj), + JSHandle(thread, value)); + return true; + } + JSHandle objHandle(thread, obj); + JSHandle valueHandle(thread, value); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSTaggedValue key(factory->ResolveString(stringId)); + return StoreMissByName(thread, JSHandle(thread, obj), JSHandle(thread, key), + JSHandle(thread, value), slotId); +} + +bool ICAccessor::StoreGlobalPrototypeByName(JSThread *thread, JSTaggedValue obj, uint32_t stringId, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId) +{ + ASSERT(handler.IsPrototypeHandler()); + PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (JSHClass::HasProtoChainChanged(prototypeHandler->GetProtoCell())) { + JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo(); + ASSERT(handlerInfo.IsGlobalHandler()); + GlobalHandler *globalHandler = GlobalHandler::Cast(handlerInfo.GetTaggedObject()); + PropertyBox *cell = PropertyBox::Cast(globalHandler->GetPropertyBox().GetTaggedObject()); + auto *setter = AccessorData::Cast(cell->GetValue().GetTaggedObject()); + JSObject::CallSetter(thread, *setter, JSHandle(thread, obj), + JSHandle(thread, value)); + return true; + } + JSHandle valueHandle(thread, value); + JSTaggedValue key(factory->ResolveString(stringId)); + return StoreGlobalMissByName(thread, JSHandle(thread, key), JSHandle(thread, value), + slotId); +} + +AccessorData *ICAccessor::GetAccessor([[maybe_unused]] JSThread *thread, JSObject *obj, uint32_t handler) +{ + DISALLOW_GARBAGE_COLLECTION; + JSTaggedValue accessor = JSTaggedValue::Undefined(); + int index = HandlerBase::GetOffset(handler); + if (HandlerBase::IsInlinedProps(handler)) { + accessor = obj->GetPropertyInlinedProps(index); + } else if (HandlerBase::IsNonInlinedProps(handler)) { + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + accessor = array->Get(index); + } + ASSERT(!accessor.IsUndefined()); + return AccessorData::Cast(accessor.GetTaggedObject()); +} + +bool ICAccessor::StoreGlobalICByValue(JSThread *thread, JSTaggedValue key, JSTaggedValue value, uint16_t slotId) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(JSHandle(thread, key)), "Key is not a property key"); + + if (key.IsNumber()) { + JSTaggedValue globalValue = thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject(); + return JSTaggedValue::SetProperty(thread, JSHandle(thread, globalValue), + JSHandle(thread, key), JSHandle(thread, value)); + } + FunctionCache *cache = FunctionCache::GetCurrent(thread); + JSTaggedValue handler = cache->GetGlobalHandlerByValue(key, slotId); + bool success = false; + if (!handler.IsNull()) { + JSTaggedValue globalValue = thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject(); + success = StoreGlobalWithHandlerByValue(thread, globalValue, key, value, handler, slotId); + } else { + success = StoreGlobalMissByValue(thread, JSHandle(thread, key), + JSHandle(thread, value), slotId); + } + return success; +} + +JSTaggedValue ICAccessor::LoadGlobalIC(JSThread *thread, JSTaggedValue key, uint16_t slotId) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(JSHandle(thread, key)), "Key is not a property key"); + JSTaggedValue globalValue = thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject(); + if (key.IsNumber()) { + return FastRuntimeStub::FastGetPropertyByIndex(thread, globalValue, key.GetArrayLength()); + } + FunctionCache *cache = FunctionCache::GetCurrent(thread); + JSTaggedValue handler = cache->GetGlobalHandlerByValue(key, slotId); + if (!handler.IsNull()) { + JSTaggedValue ret = LoadGlobalWithHandler(thread, globalValue, handler); + if (!ret.IsHole()) { + return ret; + } + } + return LoadGlobalMiss(thread, JSHandle(thread, key), slotId); +} + +JSTaggedValue ICAccessor::LoadGlobalICByName(JSThread *thread, uint32_t stringId, uint16_t slotId) +{ + FunctionCache *cache = FunctionCache::GetCurrent(thread); + JSTaggedValue handler = cache->GetGlobalHandlerByIndex(slotId); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (!handler.IsNull()) { + JSTaggedValue globalValue = thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject(); + JSTaggedValue ret = LoadGlobalWithHandler(thread, globalValue, handler); + if (!ret.IsHole()) { + return ret; + } + } + JSTaggedValue key(factory->ResolveString(stringId)); + return LoadGlobalMissByName(thread, JSHandle(thread, key), slotId); +} +} // namespace panda::ecmascript diff --git a/ecmascript/ic/ic_accessor.h b/ecmascript/ic/ic_accessor.h new file mode 100644 index 0000000000000000000000000000000000000000..57cab7b4307155fc340cea45d56a7543999c2fa7 --- /dev/null +++ b/ecmascript/ic/ic_accessor.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_IC_ACCESSOR_H +#define PANDA_RUNTIME_ECMASCRIPT_IC_ACCESSOR_H + +#include "ecmascript/accessor_data.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class ICAccessor { +public: + static JSTaggedValue LoadIC(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, uint16_t slotId); + + static JSTaggedValue LoadGlobalIC(JSThread *thread, JSTaggedValue key, uint16_t slotId); + + static JSTaggedValue LoadGlobalICByName(JSThread *thread, uint32_t stringId, uint16_t slotId); + + static JSTaggedValue LoadICByName(JSThread *thread, JSTaggedValue receiver, uint32_t stringId, uint16_t slotId); + // dispatch + static JSTaggedValue LoadWithHandler(JSThread *thread, JSObject *obj, JSTaggedValue receiver, + JSTaggedValue handler); + + static JSTaggedValue LoadGlobalWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue handler); + + static JSTaggedValue LoadProperty(JSThread *thread, JSObject *obj, JSTaggedValue receiver, JSTaggedValue handler); + + static JSTaggedValue LoadGlobal(JSThread *thread, JSTaggedValue receiver, JSTaggedValue handler); + + static JSTaggedValue LoadPrototype(JSThread *thread, JSObject *obj, JSTaggedValue handler); + + static JSTaggedValue LoadGlobalPrototype(JSThread *thread, JSTaggedValue obj, JSTaggedValue handler); + + static JSTaggedValue LoadNonExistent(JSTaggedValue handler); + + static bool StoreIC(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, JSTaggedValue value, + uint16_t slotId); + + static bool StoreGlobalICByName(JSThread *thread, uint32_t stringId, JSTaggedValue value, uint16_t slotId); + + static bool StoreGlobalICByValue(JSThread *thread, JSTaggedValue key, JSTaggedValue value, uint16_t slotId); + + static bool StoreICByName(JSThread *thread, JSTaggedValue receiver, uint32_t stringId, JSTaggedValue value, + uint16_t slotId); + + static bool StoreWithHandler(JSThread *thread, JSObject *obj, JSTaggedValue key, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId); + + static bool StoreWithHandlerByName(JSThread *thread, JSObject *obj, uint32_t stringId, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId); + + static bool StoreGlobalWithHandlerByName(JSThread *thread, JSTaggedValue obj, uint32_t stringId, + JSTaggedValue value, JSTaggedValue handler, uint16_t slotId); + + static bool StoreGlobalWithHandlerByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, + JSTaggedValue value, JSTaggedValue handler, uint16_t slotId); + + static void StoreProperty(JSThread *thread, JSObject *obj, JSTaggedValue value, uint32_t handler); + + static void StoreField(const JSThread *thread, JSObject *obj, JSTaggedValue value, uint32_t handler); + + static bool StoreGlobal(JSThread *thread, JSObject *obj, JSTaggedValue key, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId); + + static bool StoreGlobalByName(JSThread *thread, JSObject *obj, uint32_t stringId, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId); + + static bool StoreGlobalByValue(JSThread *thread, JSObject *obj, JSTaggedValue key, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId); + + static void StoreWithTransition(const JSThread *thread, JSObject *obj, JSTaggedValue value, JSTaggedValue handler); + + static bool StorePrototype(JSThread *thread, JSObject *obj, JSTaggedValue key, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId); + + static bool StorePrototypeByName(JSThread *thread, JSObject *obj, uint32_t stringId, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId); + + static bool StoreGlobalPrototypeByName(JSThread *thread, JSTaggedValue obj, uint32_t stringId, JSTaggedValue value, + JSTaggedValue handler, uint16_t slotId); + + static inline bool StoreGlobalPrototypeByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, + JSTaggedValue value, JSTaggedValue handler, uint16_t slotId); + + static AccessorData *GetAccessor(JSThread *thread, JSObject *obj, uint32_t handler); + + static AccessorData *GetInternelAccessor(JSObject *obj, uint32_t handler); + + static JSTaggedValue LoadMiss(JSThread *thread, const JSHandle &obj, const JSHandle &key, + uint16_t slotId); + + static JSTaggedValue LoadGlobalMiss(JSThread *thread, const JSHandle &key, uint16_t slotId); + + static JSTaggedValue LoadGlobalMissByName(JSThread *thread, const JSHandle &key, uint16_t slotId); + + static bool StoreMiss(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, uint16_t slotId); + + static JSTaggedValue LoadMissByName(JSThread *thread, const JSHandle &obj, + const JSHandle &key, uint16_t slotId); + + static bool StoreMissByName(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, uint16_t slotId); + static bool StoreGlobalMissByName(JSThread *thread, const JSHandle &key, + const JSHandle &value, uint16_t slotId); + + static bool StoreGlobalMissByValue(JSThread *thread, const JSHandle &key, + const JSHandle &value, uint16_t slotId); +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_IC_ACCESSOR_H diff --git a/ecmascript/ic/ic_handler-inl.h b/ecmascript/ic/ic_handler-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..effd3ada1496be2521fd547c7d24575c284d4ee8 --- /dev/null +++ b/ecmascript/ic/ic_handler-inl.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_IC_HANDLER_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_IC_HANDLER_INL_H + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_object-inl.h" +#include "ic_handler.h" + +namespace panda::ecmascript { +JSTaggedValue LoadHandler::LoadElement() +{ + uint32_t handler = 0; + KindBit::Set(HandlerKind::ELEMENT, &handler); + return JSTaggedValue(handler); +} + +JSTaggedValue LoadHandler::LoadProperty(const ObjectOperator &op) +{ + uint32_t handler = 0; + ASSERT(!op.IsElement()); + if (!op.IsFound()) { + KindBit::Set(HandlerKind::NON_EXIST, &handler); + return JSTaggedValue(handler); + } + ASSERT(op.IsFastMode()); + + JSTaggedValue val = op.GetValue(); + if (val.IsPropertyBox()) { + return val; + } + bool hasAccessor = op.IsAccessorDescriptor(); + AccessorBit::Set(hasAccessor, &handler); + if (!hasAccessor) { + KindBit::Set(HandlerKind::FIELD, &handler); + } + + if (op.IsInlinedProps()) { + InlinedPropsBit::Set(true, &handler); + JSHandle holder = JSHandle::Cast(op.GetHolder()); + auto index = holder->GetPropertyInObjectIndex(op.GetIndex()); + OffsetBit::Set(index, &handler); + return JSTaggedValue(handler); + } + if (op.IsFastMode()) { + OffsetBit::Set(op.GetIndex(), &handler); + return JSTaggedValue(handler); + } + UNREACHABLE(); +} + +inline JSTaggedValue PrototypeHandler::LoadPrototype(const JSThread *thread, const ObjectOperator &op, + const JSHandle &hclass) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSTaggedValue handlerInfo = LoadHandler::LoadProperty(op); + PrototypeHandler *handler = factory->NewPrototypeHandler(); + handler->SetHandlerInfo(thread, handlerInfo); + if (op.IsFound()) { + handler->SetHolder(thread, op.GetHolder()); + } + auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); + handler->SetProtoCell(thread, result); + return JSTaggedValue(handler); +} + +inline JSTaggedValue PrototypeHandler::StorePrototype(const JSThread *thread, const ObjectOperator &op, + const JSHandle &hclass) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + PrototypeHandler *handler = factory->NewPrototypeHandler(); + JSTaggedValue handlerInfo = StoreHandler::StoreProperty(op); + handler->SetHandlerInfo(thread, handlerInfo); + handler->SetHolder(thread, op.GetHolder()); + auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); + handler->SetProtoCell(thread, result); + return JSTaggedValue(handler); +} + +JSTaggedValue StoreHandler::StoreElement(JSHandle receiver) +{ + uint32_t handler = 0; + KindBit::Set(HandlerKind::ELEMENT, &handler); + + if (receiver->IsJSArray()) { + IsJSArrayBit::Set(true, &handler); + } + return JSTaggedValue(handler); +} + +JSTaggedValue StoreHandler::StoreProperty(const ObjectOperator &op) +{ + ASSERT(!op.IsElement()); + uint32_t handler = 0; + JSTaggedValue val = op.GetValue(); + if (val.IsPropertyBox()) { + return val; + } + bool hasSetter = op.IsAccessorDescriptor(); + AccessorBit::Set(hasSetter, &handler); + if (!hasSetter) { + KindBit::Set(HandlerKind::FIELD, &handler); + } + if (op.IsInlinedProps()) { + InlinedPropsBit::Set(true, &handler); + JSHandle receiver = JSHandle::Cast(op.GetReceiver()); + auto index = receiver->GetPropertyInObjectIndex(op.GetIndex()); + OffsetBit::Set(index, &handler); + return JSTaggedValue(handler); + } + ASSERT(op.IsFastMode()); + OffsetBit::Set(op.GetIndex(), &handler); + return JSTaggedValue(handler); +} + +inline JSTaggedValue TransitionHandler::StoreTransition(const JSThread *thread, const ObjectOperator &op) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + TransitionHandler *handler = factory->NewTransitionHandler(); + JSTaggedValue handlerInfo = StoreHandler::StoreProperty(op); + handler->SetHandlerInfo(thread, handlerInfo); + auto hclass = JSObject::Cast(op.GetReceiver()->GetHeapObject())->GetJSHClass(); + handler->SetTransitionHClass(thread, JSTaggedValue(hclass)); + return JSTaggedValue(handler); +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_IC_HANDLER_INL_H diff --git a/ecmascript/ic/ic_handler.h b/ecmascript/ic/ic_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..91f193f085a01562f6f6bcc9b2efb1c29dc8b0e0 --- /dev/null +++ b/ecmascript/ic/ic_handler.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_IC_HANDLER_H +#define PANDA_RUNTIME_ECMASCRIPT_IC_HANDLER_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/mem/tagged_object.h" + +namespace panda::ecmascript { +class HandlerBase { +public: + static constexpr uint32_t KIND_BIT_LENGTH = 3; + enum HandlerKind { + NONE = 0, + FIELD, + ELEMENT, + DICTIONARY, + NON_EXIST, + }; + + using KindBit = BitField; + using InlinedPropsBit = KindBit::NextFlag; + using AccessorBit = InlinedPropsBit::NextFlag; + using InternalAccessorBit = AccessorBit::NextFlag; + using IsJSArrayBit = InternalAccessorBit::NextFlag; + using OffsetBit = IsJSArrayBit::NextField; + + HandlerBase() = default; + virtual ~HandlerBase() = default; + + static inline bool IsAccessor(uint32_t handler) + { + return AccessorBit::Get(handler); + } + + static inline bool IsInternalAccessor(uint32_t handler) + { + return InternalAccessorBit::Get(handler); + } + + static inline bool IsNonExist(uint32_t handler) + { + return GetKind(handler) == HandlerKind::NON_EXIST; + } + + static inline bool IsField(uint32_t handler) + { + return GetKind(handler) == HandlerKind::FIELD; + } + + static inline bool IsElement(uint32_t handler) + { + return GetKind(handler) == HandlerKind::ELEMENT; + } + + static inline bool IsDictionary(uint32_t handler) + { + return GetKind(handler) == HandlerKind::DICTIONARY; + } + + static inline bool IsInlinedProps(uint32_t handler) + { + return InlinedPropsBit::Get(handler); + } + + static inline HandlerKind GetKind(uint32_t handler) + { + return KindBit::Get(handler); + } + + static inline bool IsJSArray(uint32_t handler) + { + return IsJSArrayBit::Get(handler); + } + + static inline int GetOffset(uint32_t handler) + { + return OffsetBit::Get(handler); + } +}; + +class LoadHandler final : public HandlerBase { +public: + static inline JSTaggedValue LoadProperty(const ObjectOperator &op); + static inline JSTaggedValue LoadElement(); +}; + +class StoreHandler final : public HandlerBase { +public: + static inline JSTaggedValue StoreProperty(const ObjectOperator &op); + static inline JSTaggedValue StoreElement(JSHandle receiver); +}; + +class TransitionHandler : public TaggedObject { +public: + static TransitionHandler *Cast(TaggedObject *object) + { + ASSERT(JSTaggedValue(object).IsTransitionHandler()); + return static_cast(object); + } + + static inline JSTaggedValue StoreTransition(const JSThread *thread, const ObjectOperator &op); + + static constexpr size_t HANDLER_INFO_OFFSET = sizeof(TaggedObject); + ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, TRANSITION_HCLASS_OFFSET) + + ACCESSORS(TransitionHClass, TRANSITION_HCLASS_OFFSET, SIZE) + + DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE) +}; + +class PrototypeHandler : public TaggedObject { +public: + static PrototypeHandler *Cast(TaggedObject *object) + { + ASSERT(JSTaggedValue(object).IsPrototypeHandler()); + return static_cast(object); + } + + static inline JSTaggedValue LoadPrototype(const JSThread *thread, const ObjectOperator &op, + const JSHandle &hclass); + static inline JSTaggedValue StorePrototype(const JSThread *thread, const ObjectOperator &op, + const JSHandle &hclass); + + static constexpr size_t HANDLER_INFO_OFFSET = sizeof(TaggedObject); + + ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, PROTO_CELL_OFFSET) + + ACCESSORS(ProtoCell, PROTO_CELL_OFFSET, HOLDER_OFFSET) + + ACCESSORS(Holder, HOLDER_OFFSET, SIZE) + + DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE) +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_IC_HANDLER_H diff --git a/ecmascript/ic/ic_runtime.cpp b/ecmascript/ic/ic_runtime.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ee47519aac9ba5c91a17eba46fded1ec7d33691e --- /dev/null +++ b/ecmascript/ic/ic_runtime.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/global_dictionary-inl.h" +#include "ecmascript/global_env.h" +#include "ecmascript/ic/ic_handler-inl.h" +#include "ecmascript/ic/ic_runtime.h" +#include "ecmascript/ic/profile_type_info.h" +#include "ecmascript/interpreter/slow_runtime_stub.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/js_proxy.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_typed_array.h" +#include "ecmascript/object_factory-inl.h" + +namespace panda::ecmascript { +#define TRACE_IC 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) + +void ICRuntime::UpdateLoadHandler(const ObjectOperator &op, JSHandle key, + JSHandle receiver) +{ + if (icAccessor_.GetICState() == ProfileTypeAccessor::ICState::MEGA) { + return; + } + if (IsNamedIC(GetICKind())) { + key = JSHandle(); + } + JSTaggedValue handlerValue; + JSHandle hclass(GetThread(), JSHandle::Cast(receiver)->GetClass()); + if (op.IsElement()) { + handlerValue = LoadHandler::LoadElement(); + } else { + if (!op.IsFound()) { + handlerValue = PrototypeHandler::LoadPrototype(thread_, op, hclass); + } else if (!op.IsOnPrototype()) { + handlerValue = LoadHandler::LoadProperty(op); + } else { + // do not support global prototype ic + if (IsGlobalLoadIC(GetICKind())) { + return; + } + handlerValue = PrototypeHandler::LoadPrototype(thread_, op, hclass); + } + } + + auto handler = JSHandle(thread_, handlerValue); + if (key.IsEmpty()) { + icAccessor_.AddHandlerWithoutKey(JSHandle::Cast(hclass), handler); + } else if (op.IsElement()) { + // do not support global element ic + if (IsGlobalLoadIC(GetICKind())) { + return; + } + icAccessor_.AddElementHandler(JSHandle::Cast(hclass), handler); + } else { + icAccessor_.AddHandlerWithKey(key, JSHandle::Cast(hclass), handler); + } +} + +void ICRuntime::UpdateStoreHandler(const ObjectOperator &op, JSHandle key, + JSHandle receiver) +{ + if (icAccessor_.GetICState() == ProfileTypeAccessor::ICState::MEGA) { + return; + } + if (IsNamedIC(GetICKind())) { + key = JSHandle(); + } + JSTaggedValue handlerValue; + if (op.IsElement()) { + handlerValue = StoreHandler::StoreElement(receiver); + } else { + ASSERT(op.IsFound()); + if (op.IsOnPrototype()) { + // do not support global prototype ic + if (IsGlobalStoreIC(GetICKind())) { + return; + } + JSHandle hclass(GetThread(), JSHandle::Cast(receiver)->GetClass()); + handlerValue = PrototypeHandler::StorePrototype(thread_, op, hclass); + } else if (op.IsTransition()) { + handlerValue = TransitionHandler::StoreTransition(thread_, op); + } else { + handlerValue = StoreHandler::StoreProperty(op); + } + } + + auto handler = JSHandle(thread_, handlerValue); + if (key.IsEmpty()) { + icAccessor_.AddHandlerWithoutKey(receiverHClass_, handler); + } else if (op.IsElement()) { + // do not support global element ic + if (IsGlobalStoreIC(GetICKind())) { + return; + } + icAccessor_.AddElementHandler(receiverHClass_, handler); + } else { + icAccessor_.AddHandlerWithKey(key, receiverHClass_, handler); + } +} + +void ICRuntime::TraceIC([[maybe_unused]] JSHandle receiver, + [[maybe_unused]] JSHandle key) const +{ +#if TRACE_IC + auto kind = ICKindToString(GetICKind()); + auto state = ProfileTypeAccessor::ICStateToString(icAccessor_.GetICState()); + if (key->IsString()) { + LOG(ERROR, RUNTIME) << kind << " miss key is: " << JSHandle::Cast(key)->GetCString().get() + << ", receiver is " << receiver->GetHeapObject()->GetClass()->IsDictionaryMode() + << ", state is " << state; + } else { + LOG(ERROR, RUNTIME) << kind << " miss " << ", state is " + << ", receiver is " << receiver->GetHeapObject()->GetClass()->IsDictionaryMode() + << state; + } +#endif +} + +JSTaggedValue LoadICRuntime::LoadMiss(JSHandle receiver, JSHandle key) +{ + if (!receiver->IsJSObject()) { + return JSTaggedValue::GetProperty(GetThread(), receiver, key).GetValue().GetTaggedValue(); + } + ObjectOperator op(GetThread(), receiver, key); + auto result = JSObject::GetProperty(GetThread(), &op); + if (!op.IsFound() && GetICKind() == ICKind::NamedGlobalLoadIC) { + return SlowRuntimeStub::ThrowReferenceError(GetThread(), key.GetTaggedValue(), " is not defined"); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread()); + // ic-switch + if (!GetThread()->GetEcmaVM()->ICEnable()) { + icAccessor_.SetAsMega(); + return result; + } +#ifndef NDEBUG + TraceIC(receiver, key); +#endif + // do not cache element + if (!op.IsFastMode() && op.IsFound()) { + icAccessor_.SetAsMega(); + return result; + } + + UpdateLoadHandler(op, key, receiver); + return result; +} + +JSTaggedValue StoreICRuntime::StoreMiss(JSHandle receiver, JSHandle key, + JSHandle value) +{ + if (!receiver->IsJSObject()) { + bool success = JSTaggedValue::SetProperty(GetThread(), receiver, key, value, true); + return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); + } + UpdateReceiverHClass(JSHandle(GetThread(), JSHandle::Cast(receiver)->GetClass())); + ObjectOperator op(GetThread(), receiver, key); + bool success = JSObject::SetProperty(&op, value, true); + if (!success && GetICKind() == ICKind::NamedGlobalStoreIC) { + return SlowRuntimeStub::ThrowReferenceError(GetThread(), key.GetTaggedValue(), " is not defined"); + } + // ic-switch + if (!GetThread()->GetEcmaVM()->ICEnable()) { + icAccessor_.SetAsMega(); + return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); + } +#ifndef NDEBUG + TraceIC(receiver, key); +#endif + // do not cache element + if (!op.IsFastMode()) { + icAccessor_.SetAsMega(); + return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); + } + UpdateStoreHandler(op, key, receiver); + return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/ic/ic_runtime.h b/ecmascript/ic/ic_runtime.h new file mode 100644 index 0000000000000000000000000000000000000000..3fe5320e8c6253998fa41360fce986863897937e --- /dev/null +++ b/ecmascript/ic/ic_runtime.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_IC_IC_RUNTIME_H +#define PANDA_RUNTIME_ECMASCRIPT_IC_IC_RUNTIME_H + +#include "ecmascript/ic/profile_type_info.h" +#include "ecmascript/accessor_data.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_handle.h" + +namespace panda::ecmascript { +class ProfileTypeInfo; +class JSThread; +class ObjectOperator; + +class ICRuntime { +public: + ICRuntime(JSThread *thread, JSHandle profileTypeInfo, uint32_t slotId, ICKind kind) + : thread_(thread), icAccessor_(thread, profileTypeInfo, slotId, kind) + { + } + + ~ICRuntime() = default; + + void UpdateLoadHandler(const ObjectOperator &op, JSHandle key, JSHandle receiver); + void UpdateStoreHandler(const ObjectOperator &op, JSHandle key, JSHandle receiver); + + JSThread *GetThread() const + { + return thread_; + } + + void UpdateReceiverHClass(JSHandle receiverHClass) + { + receiverHClass_ = receiverHClass; + } + + ICKind GetICKind() const + { + return icAccessor_.GetKind(); + } + + void TraceIC(JSHandle receiver, JSHandle key) const; + +protected: + JSThread *thread_; + JSHandle receiverHClass_{}; + ProfileTypeAccessor icAccessor_; +}; + +class LoadICRuntime : public ICRuntime { +public: + LoadICRuntime(JSThread *thread, JSHandle profileTypeInfo, uint32_t slotId, ICKind kind) + : ICRuntime(thread, profileTypeInfo, slotId, kind) + { + } + + ~LoadICRuntime() = default; + + JSTaggedValue LoadMiss(JSHandle receiver, JSHandle key); +}; + +class StoreICRuntime : public ICRuntime { +public: + StoreICRuntime(JSThread *thread, JSHandle profileTypeInfo, uint32_t slotId, ICKind kind) + : ICRuntime(thread, profileTypeInfo, slotId, kind) + { + } + + ~StoreICRuntime() = default; + + JSTaggedValue StoreMiss(JSHandle receiver, JSHandle key, + JSHandle value); +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_IC_IC_RUNTIME_H diff --git a/ecmascript/ic/profile_type_info.cpp b/ecmascript/ic/profile_type_info.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9091b8970006055eba9d2cdd802a1f195b951006 --- /dev/null +++ b/ecmascript/ic/profile_type_info.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ic/ic_handler-inl.h" +#include "ecmascript/ic/profile_type_info.h" +#include "ecmascript/js_function.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript { +void ProfileTypeAccessor::AddElementHandler(JSHandle dynclass, JSHandle handler) const +{ + auto profileData = profileTypeInfo_->Get(slotId_); + ASSERT(!profileData.IsHole()); + auto index = slotId_; + if (profileData.IsUndefined()) { + profileTypeInfo_->Set(thread_, index, GetWeakRef(dynclass.GetTaggedValue())); + profileTypeInfo_->Set(thread_, index + 1, handler.GetTaggedValue()); + return; + } + // clear key ic + if (!profileData.IsWeak() && (profileData.IsString() || profileData.IsSymbol())) { + profileTypeInfo_->Set(thread_, index, GetWeakRef(dynclass.GetTaggedValue())); + profileTypeInfo_->Set(thread_, index + 1, handler.GetTaggedValue()); + return; + } + AddHandlerWithoutKey(dynclass, handler); +} + +void ProfileTypeAccessor::AddHandlerWithoutKey(JSHandle dynclass, JSHandle handler) const +{ + auto index = slotId_; + if (IsNamedGlobalIC(GetKind())) { + profileTypeInfo_->Set(thread_, index, handler.GetTaggedValue()); + return; + } + auto profileData = profileTypeInfo_->Get(slotId_); + ASSERT(!profileData.IsHole()); + if (profileData.IsUndefined()) { + ASSERT(profileTypeInfo_->Get(index + 1) == JSTaggedValue::Undefined()); + profileTypeInfo_->Set(thread_, index, GetWeakRef(dynclass.GetTaggedValue())); + profileTypeInfo_->Set(thread_, index + 1, handler.GetTaggedValue()); + return; + } + if (!profileData.IsWeak() && profileData.IsTaggedArray()) { // POLY + ASSERT(profileTypeInfo_->Get(index + 1) == JSTaggedValue::Hole()); + JSHandle arr(thread_, profileData); + const array_size_t step = 2; + array_size_t newLen = arr->GetLength() + step; + if (newLen > CACHE_MAX_LEN) { + profileTypeInfo_->Set(thread_, index, JSTaggedValue::Hole()); + profileTypeInfo_->Set(thread_, index + 1, JSTaggedValue::Hole()); + return; + } + auto factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle newArr = factory->NewTaggedArray(newLen); + array_size_t i = 0; + for (; i < arr->GetLength(); i += step) { + newArr->Set(thread_, i, arr->Get(i)); + newArr->Set(thread_, i + 1, arr->Get(i + 1)); + } + newArr->Set(thread_, i, GetWeakRef(dynclass.GetTaggedValue())); + newArr->Set(thread_, i + 1, handler.GetTaggedValue()); + profileTypeInfo_->Set(thread_, index, newArr.GetTaggedValue()); + profileTypeInfo_->Set(thread_, index + 1, JSTaggedValue::Hole()); + return; + } + // MONO to POLY + auto factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle newArr = factory->NewTaggedArray(POLY_DEFAULT_LEN); + array_size_t arrIndex = 0; + newArr->Set(thread_, arrIndex++, profileTypeInfo_->Get(index)); + newArr->Set(thread_, arrIndex++, profileTypeInfo_->Get(index + 1)); + newArr->Set(thread_, arrIndex++, GetWeakRef(dynclass.GetTaggedValue())); + newArr->Set(thread_, arrIndex, handler.GetTaggedValue()); + + profileTypeInfo_->Set(thread_, index, newArr.GetTaggedValue()); + profileTypeInfo_->Set(thread_, index + 1, JSTaggedValue::Hole()); +} + +void ProfileTypeAccessor::AddHandlerWithKey(JSHandle key, JSHandle dynclass, + JSHandle handler) const +{ + if (IsValueGlobalIC(GetKind())) { + AddGlobalHandlerKey(key, handler); + return; + } + auto profileData = profileTypeInfo_->Get(slotId_); + ASSERT(!profileData.IsHole()); + auto index = slotId_; + if (profileData.IsUndefined()) { + ASSERT(profileTypeInfo_->Get(index + 1) == JSTaggedValue::Undefined()); + profileTypeInfo_->Set(thread_, index, key.GetTaggedValue()); + const int arrayLength = 2; + JSHandle newArr = thread_->GetEcmaVM()->GetFactory()->NewTaggedArray(arrayLength); + newArr->Set(thread_, 0, GetWeakRef(dynclass.GetTaggedValue())); + newArr->Set(thread_, 1, handler.GetTaggedValue()); + profileTypeInfo_->Set(thread_, index + 1, newArr.GetTaggedValue()); + return; + } + // for element ic, profileData may dynclass or taggedarray + if (key.GetTaggedValue() != profileData) { + profileTypeInfo_->Set(thread_, index, JSTaggedValue::Hole()); + profileTypeInfo_->Set(thread_, index + 1, JSTaggedValue::Hole()); + return; + } + JSTaggedValue patchValue = profileTypeInfo_->Get(index + 1); + ASSERT(patchValue.IsTaggedArray()); + JSHandle arr(thread_, patchValue); + const array_size_t step = 2; + if (arr->GetLength() > step) { // POLY + array_size_t newLen = arr->GetLength() + step; + if (newLen > CACHE_MAX_LEN) { + profileTypeInfo_->Set(thread_, index, JSTaggedValue::Hole()); + profileTypeInfo_->Set(thread_, index + 1, JSTaggedValue::Hole()); + return; + } + auto factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle newArr = factory->NewTaggedArray(newLen); + newArr->Set(thread_, 0, GetWeakRef(dynclass.GetTaggedValue())); + newArr->Set(thread_, 1, handler.GetTaggedValue()); + for (array_size_t i = 0; i < arr->GetLength(); i += step) { + newArr->Set(thread_, i + step, arr->Get(i)); + newArr->Set(thread_, i + step + 1, arr->Get(i + 1)); + } + profileTypeInfo_->Set(thread_, index + 1, newArr.GetTaggedValue()); + return; + } + // MONO + auto factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle newArr = factory->NewTaggedArray(POLY_DEFAULT_LEN); + array_size_t arrIndex = 0; + newArr->Set(thread_, arrIndex++, arr->Get(0)); + newArr->Set(thread_, arrIndex++, arr->Get(1)); + newArr->Set(thread_, arrIndex++, GetWeakRef(dynclass.GetTaggedValue())); + newArr->Set(thread_, arrIndex++, handler.GetTaggedValue()); + + profileTypeInfo_->Set(thread_, index + 1, newArr.GetTaggedValue()); +} + +void ProfileTypeAccessor::AddGlobalHandlerKey(JSHandle key, JSHandle handler) const +{ + auto index = slotId_; + const uint8_t step = 2; // key and value pair + JSTaggedValue indexVal = profileTypeInfo_->Get(index); + if (indexVal.IsUndefined()) { + auto factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle newArr = factory->NewTaggedArray(step); + newArr->Set(thread_, 0, GetWeakRef(key.GetTaggedValue())); + newArr->Set(thread_, 1, handler.GetTaggedValue()); + profileTypeInfo_->Set(thread_, index, newArr.GetTaggedValue()); + return; + } + ASSERT(indexVal.IsTaggedArray()); + JSHandle arr(thread_, indexVal); + array_size_t newLen = arr->GetLength() + step; + if (newLen > CACHE_MAX_LEN) { + profileTypeInfo_->Set(thread_, index, JSTaggedValue::Hole()); + return; + } + auto factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle newArr = factory->NewTaggedArray(newLen); + newArr->Set(thread_, 0, GetWeakRef(key.GetTaggedValue())); + newArr->Set(thread_, 1, handler.GetTaggedValue()); + + for (array_size_t i = 0; i < arr->GetLength(); i += step) { + newArr->Set(thread_, i + step, arr->Get(i)); + newArr->Set(thread_, i + step + 1, arr->Get(i + 1)); + } + profileTypeInfo_->Set(thread_, index, newArr.GetTaggedValue()); +} + +void ProfileTypeAccessor::SetAsMega() const +{ + profileTypeInfo_->Set(thread_, slotId_, JSTaggedValue::Hole()); + profileTypeInfo_->Set(thread_, slotId_ + 1, JSTaggedValue::Hole()); +} + +std::string ICKindToString(ICKind kind) +{ + switch (kind) { + case ICKind::NamedLoadIC: + return "NamedLoadIC"; + case ICKind::NamedStoreIC: + return "NamedStoreIC"; + case ICKind::LoadIC: + return "LoadIC"; + case ICKind::StoreIC: + return "StoreIC"; + case ICKind::NamedGlobalLoadIC: + return "NamedGlobalLoadIC"; + case ICKind::NamedGlobalStoreIC: + return "NamedGlobalStoreIC"; + case ICKind::GlobalLoadIC: + return "GlobalLoadIC"; + case ICKind::GlobalStoreIC: + return "GlobalStoreIC"; + default: + UNREACHABLE(); + break; + } +} + +std::string ProfileTypeAccessor::ICStateToString(ProfileTypeAccessor::ICState state) +{ + switch (state) { + case ICState::UNINIT: + return "uninit"; + case ICState::MONO: + return "mono"; + case ICState::POLY: + return "poly"; + case ICState::MEGA: + return "mega"; + default: + UNREACHABLE(); + break; + } +} + +ProfileTypeAccessor::ICState ProfileTypeAccessor::GetICState() const +{ + auto profileData = profileTypeInfo_->Get(slotId_); + if (profileData.IsUndefined()) { + return ICState::UNINIT; + } + + if (profileData.IsHole()) { + return ICState::MEGA; + } + + switch (kind_) { + case ICKind::NamedLoadIC: + case ICKind::NamedStoreIC: + if (profileData.IsWeak()) { + return ICState::MONO; + } + ASSERT(profileData.IsTaggedArray()); + return ICState::POLY; + case ICKind::LoadIC: + case ICKind::StoreIC: { + if (profileData.IsWeak()) { + return ICState::MONO; + } + if (profileData.IsTaggedArray()) { + TaggedArray *array = TaggedArray::Cast(profileData.GetHeapObject()); + return array->GetLength() == 2 ? ICState::MONO : ICState::POLY; // 2 : test case + } + profileData = profileTypeInfo_->Get(slotId_ + 1); + TaggedArray *array = TaggedArray::Cast(profileData.GetHeapObject()); + return array->GetLength() == 2 ? ICState::MONO : ICState::POLY; // 2 : test case + } + case ICKind::NamedGlobalLoadIC: + case ICKind::NamedGlobalStoreIC: + ASSERT(profileData.IsPropertyBox()); + return ICState::MONO; + case ICKind::GlobalLoadIC: + case ICKind::GlobalStoreIC: { + ASSERT(profileData.IsTaggedArray()); + TaggedArray *array = TaggedArray::Cast(profileData.GetHeapObject()); + return array->GetLength() == 2 ? ICState::MONO : ICState::POLY; // 2 : test case + } + default: + UNREACHABLE(); + break; + } + return ICState::UNINIT; +} +} // namespace panda::ecmascript diff --git a/ecmascript/ic/profile_type_info.h b/ecmascript/ic/profile_type_info.h new file mode 100644 index 0000000000000000000000000000000000000000..3d98d0a2abfbb3dc0287161bb98f7f35caf802ef --- /dev/null +++ b/ecmascript/ic/profile_type_info.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_PROFILE_TYPE_INFO_H +#define PANDA_RUNTIME_PROFILE_TYPE_INFO_H + +#include "ecmascript/js_function.h" +#include "ecmascript/tagged_array.h" + +namespace panda::ecmascript { +enum class ICKind { + NamedLoadIC, + NamedStoreIC, + LoadIC, + StoreIC, + NamedGlobalLoadIC, + NamedGlobalStoreIC, + GlobalLoadIC, + GlobalStoreIC, +}; + +static inline bool IsNamedGlobalIC(ICKind kind) +{ + return (kind == ICKind::NamedGlobalLoadIC) || (kind == ICKind::NamedGlobalStoreIC); +} + +static inline bool IsValueGlobalIC(ICKind kind) +{ + return (kind == ICKind::GlobalLoadIC) || (kind == ICKind::GlobalStoreIC); +} + +static inline bool IsValueNormalIC(ICKind kind) +{ + return (kind == ICKind::LoadIC) || (kind == ICKind::StoreIC); +} + +static inline bool IsValueIC(ICKind kind) +{ + return IsValueNormalIC(kind) || IsValueGlobalIC(kind); +} + +static inline bool IsNamedNormalIC(ICKind kind) +{ + return (kind == ICKind::NamedLoadIC) || (kind == ICKind::NamedStoreIC); +} + +static inline bool IsNamedIC(ICKind kind) +{ + return IsNamedNormalIC(kind) || IsNamedGlobalIC(kind); +} + +static inline bool IsGlobalLoadIC(ICKind kind) +{ + return (kind == ICKind::NamedGlobalLoadIC) || (kind == ICKind::GlobalLoadIC); +} + +static inline bool IsGlobalStoreIC(ICKind kind) +{ + return (kind == ICKind::NamedGlobalStoreIC) || (kind == ICKind::GlobalStoreIC); +} + +static inline bool IsGlobalIC(ICKind kind) +{ + return IsValueGlobalIC(kind) || IsNamedGlobalIC(kind); +} + +std::string ICKindToString(ICKind kind); + +class ProfileTypeInfo : public TaggedArray { +public: + static const array_size_t MAX_FUNC_CACHE_INDEX = std::numeric_limits::max(); + static constexpr uint32_t INVALID_SLOT_INDEX = 0xFF; + + static ProfileTypeInfo *Cast(TaggedObject *object) + { + ASSERT(JSTaggedValue(object).IsTaggedArray()); + return static_cast(object); + } +}; + + +class ProfileTypeAccessor { +public: + static constexpr size_t CACHE_MAX_LEN = 8; + static constexpr size_t POLY_DEFAULT_LEN = 4; + + enum ICState { + UNINIT, + MONO, + POLY, + MEGA, + }; + + ProfileTypeAccessor(JSThread* thread, JSHandle profileTypeInfo, uint32_t slotId, ICKind kind) + : thread_(thread), profileTypeInfo_(profileTypeInfo), slotId_(slotId), kind_(kind) + { + } + ~ProfileTypeAccessor() = default; + + ICState GetICState() const; + static std::string ICStateToString(ICState state); + void AddHandlerWithoutKey(JSHandle dynclass, JSHandle handler) const; + void AddElementHandler(JSHandle dynclass, JSHandle handler) const; + void AddHandlerWithKey(JSHandle key, JSHandle dynclass, + JSHandle handler) const; + void AddGlobalHandlerKey(JSHandle key, JSHandle handler) const; + + JSTaggedValue GetWeakRef(JSTaggedValue value) const + { + return JSTaggedValue(value.CreateAndGetWeakRef()); + } + + JSTaggedValue GetRefFromWeak(const JSTaggedValue &value) const + { + return JSTaggedValue(value.GetWeakReferent()); + } + void SetAsMega() const; + + ICKind GetKind() const + { + return kind_; + } + +private: + JSThread* thread_; + JSHandle profileTypeInfo_; + uint32_t slotId_; + ICKind kind_; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_PROFILE_TYPE_INFO_H diff --git a/ecmascript/ic/properties_cache-inl.h b/ecmascript/ic/properties_cache-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..d69a5355d71e2409b34b9ba2319619b17c4adc9d --- /dev/null +++ b/ecmascript/ic/properties_cache-inl.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_PROPERTIES_CACHE_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_PROPERTIES_CACHE_INL_H + +#include "ecmascript/ic/properties_cache.h" +#include "ecmascript/js_tagged_value-inl.h" + +namespace panda::ecmascript { +int PropertiesCache::Get(JSHClass *jsHclass, JSTaggedValue key) +{ + int hash = Hash(jsHclass, key); + PropertyKey &prop = keys_[hash]; + if ((prop.hclass_ == jsHclass) && (prop.key_ == key)) { + return keys_[hash].results_; + } + return NOT_FOUND; +} + +void PropertiesCache::Set(JSHClass *jsHclass, JSTaggedValue key, int index) +{ + int hash = Hash(jsHclass, key); + PropertyKey &prop = keys_[hash]; + prop.hclass_ = jsHclass; + prop.key_ = key; + keys_[hash].results_ = index; +} + +void PropertiesCache::Clear() +{ + for (auto &key : keys_) { + key.hclass_ = nullptr; + } +} + +int PropertiesCache::Hash(JSHClass *cls, JSTaggedValue key) +{ + uint32_t clsHash = static_cast(reinterpret_cast(cls)) >> 3U; // skip 8bytes + uint32_t keyHash = key.GetKeyHashCode(); + return static_cast((clsHash ^ keyHash) & CACHE_LENGTH_MASK); +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_PROPERTIES_CACHE_INL_H diff --git a/ecmascript/ic/properties_cache.h b/ecmascript/ic/properties_cache.h new file mode 100644 index 0000000000000000000000000000000000000000..e6ed518643cd83038b0067b57d304783afeedf7c --- /dev/null +++ b/ecmascript/ic/properties_cache.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_PROPERTIES_CACHE_H +#define PANDA_RUNTIME_ECMASCRIPT_PROPERTIES_CACHE_H + +#include + +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/ecma_macros.h" + +namespace panda::ecmascript { +class EcmaVM; +class PropertiesCache { +public: + inline int Get(JSHClass *jsHclass, JSTaggedValue key); + inline void Set(JSHClass *jsHclass, JSTaggedValue key, int index); + inline void Clear(); + + static const int NOT_FOUND = -1; + +private: + PropertiesCache() + { + for (uint32_t i = 0; i < CACHE_LENGTH; ++i) { + keys_[i].hclass_ = nullptr; + keys_[i].key_ = JSTaggedValue::Hole(); + keys_[i].results_ = NOT_FOUND; + } + } + ~PropertiesCache() = default; + + struct PropertyKey { + JSHClass *hclass_{nullptr}; + JSTaggedValue key_{JSTaggedValue::Hole()}; + int results_{NOT_FOUND}; + }; + + static inline int Hash(JSHClass *cls, JSTaggedValue key); + + static const uint32_t CACHE_LENGTH_BIT = 10; + static const uint32_t CACHE_LENGTH = (1U << CACHE_LENGTH_BIT); + static const uint32_t CACHE_LENGTH_MASK = CACHE_LENGTH - 1; + + std::array keys_{}; + + friend class EcmaVM; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_PROPERTIES_CACHE_H diff --git a/ecmascript/ic/property_box.cpp b/ecmascript/ic/property_box.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6c0068806d3a4c71ef55a88669a6e6ace437ff88 --- /dev/null +++ b/ecmascript/ic/property_box.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_tagged_value-inl.h" +#include "property_box.h" + +namespace panda::ecmascript { +void PropertyBox::Clear(const JSThread *thread) +{ + ASSERT_PRINT(!GetValue().IsHole(), "value must not be hole"); + SetValue(thread, JSTaggedValue::Hole()); +} +} // namespace panda::ecmascript diff --git a/ecmascript/ic/property_box.h b/ecmascript/ic/property_box.h new file mode 100644 index 0000000000000000000000000000000000000000..23e17f20148145b1740873a8e739d77ac99c3632 --- /dev/null +++ b/ecmascript/ic/property_box.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_PROPERTY_BOX_H +#define PANDA_RUNTIME_PROPERTY_BOX_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/property_attributes.h" + +namespace panda { +namespace ecmascript { +class PropertyBox : public TaggedObject { +public: + static PropertyBox *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsPropertyBox()); + return static_cast(object); + } + + void Clear(const JSThread *thread); + + inline bool IsInvalid() const + { + return GetValue().IsHole(); + } + + static constexpr size_t VALUE_OFFSET = sizeof(TaggedObject); + ACCESSORS(Value, VALUE_OFFSET, SIZE); + + DECL_VISIT_OBJECT(VALUE_OFFSET, SIZE) +}; +} // namespace ecmascript +} // namespace panda + +#endif \ No newline at end of file diff --git a/ecmascript/ic/proto_change_details.cpp b/ecmascript/ic/proto_change_details.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc8d0b972c9013c0db99b3840ca7c5dc47f2203f --- /dev/null +++ b/ecmascript/ic/proto_change_details.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ic/proto_change_details.h" +#include "ecmascript/weak_vector-inl.h" + +namespace panda::ecmascript { +JSHandle ChangeListener::Add(const JSThread *thread, const JSHandle &array, + const JSHandle &value, array_size_t *index) +{ + JSTaggedValue weakValue; + if (!array->Full()) { + weakValue = JSTaggedValue(value.GetTaggedValue().CreateAndGetWeakRef()); + array_size_t arrayIndex = array->PushBack(thread, weakValue); + if (arrayIndex != TaggedArray::MAX_ARRAY_INDEX) { + if (index != nullptr) { + *index = arrayIndex; + } + return array; + } + UNREACHABLE(); + } + // if exist hole, use it. + array_size_t holeIndex = CheckHole(array); + if (holeIndex != TaggedArray::MAX_ARRAY_INDEX) { + weakValue = JSTaggedValue(value.GetTaggedValue().CreateAndGetWeakRef()); + array->Set(thread, holeIndex, weakValue); + if (index != nullptr) { + *index = holeIndex; + } + return array; + } + // the vector is full and no hole exists. + JSHandle newArray = WeakVector::Grow(thread, JSHandle(array), array->GetCapacity() + 1); + weakValue = JSTaggedValue(value.GetTaggedValue().CreateAndGetWeakRef()); + array_size_t arrayIndex = newArray->PushBack(thread, weakValue); + ASSERT(arrayIndex != TaggedArray::MAX_ARRAY_INDEX); + if (index != nullptr) { + *index = arrayIndex; + } + return JSHandle(newArray); +} + +array_size_t ChangeListener::CheckHole(const JSHandle &array) +{ + for (array_size_t i = 0; i < array->GetEnd(); i++) { + JSTaggedValue value = array->Get(i); + if (value == JSTaggedValue::Hole() || value == JSTaggedValue::Undefined()) { + return i; + } + } + return TaggedArray::MAX_ARRAY_INDEX; +} + +JSTaggedValue ChangeListener::Get(array_size_t index) +{ + JSTaggedValue value = WeakVector::Get(index); + if (!value.IsHeapObject()) { + return value; + } + return JSTaggedValue(value.GetTaggedWeakRef()); +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/ic/proto_change_details.h b/ecmascript/ic/proto_change_details.h new file mode 100644 index 0000000000000000000000000000000000000000..f4a60775275ab5d377cb9398580e8a742297a364 --- /dev/null +++ b/ecmascript/ic/proto_change_details.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_PROTOTYPE_CHANGE_DETAILS_H +#define PANDA_RUNTIME_PROTOTYPE_CHANGE_DETAILS_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/weak_vector.h" + +namespace panda { +namespace ecmascript { +class ProtoChangeMarker : public TaggedObject { +public: + + using HasChangedField = BitField; + static ProtoChangeMarker *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsProtoChangeMarker()); + return static_cast(object); + } + + static constexpr size_t HAS_CHANGED_OFFSET = sizeof(TaggedObject); + SET_GET_PRIMITIVE_FIELD(HasChanged, bool, HAS_CHANGED_OFFSET, SIZE); +}; + +class ProtoChangeDetails : public TaggedObject { +public: + static constexpr int UNREGISTERED = -1; + static ProtoChangeDetails *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsProtoChangeDetails()); + return static_cast(object); + } + + static constexpr size_t CHANGE_LISTENER_OFFSET = sizeof(TaggedObject); + ACCESSORS(ChangeListener, CHANGE_LISTENER_OFFSET, REGISTER_INDEX_OFFSET); + ACCESSORS(RegisterIndex, REGISTER_INDEX_OFFSET, SIZE); + + DECL_VISIT_OBJECT(CHANGE_LISTENER_OFFSET, SIZE) +}; + +class ChangeListener : public WeakVector { +public: + static ChangeListener *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static JSHandle Add(const JSThread *thread, const JSHandle &array, + const JSHandle &value, array_size_t *index); + + static array_size_t CheckHole(const JSHandle &array); + + JSTaggedValue Get(array_size_t index); +}; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_PROTOTYPE_CHANGE_DETAILS_H diff --git a/ecmascript/interpreter/fast_runtime_stub-inl.h b/ecmascript/interpreter/fast_runtime_stub-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..7b52ff74e4f7b4f2d136dc7bd809b98f259077ca --- /dev/null +++ b/ecmascript/interpreter/fast_runtime_stub-inl.h @@ -0,0 +1,1264 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_FAST_RUNTIME_STUB_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_FAST_RUNTIME_STUB_INL_H + +#include "ecmascript/interpreter/fast_runtime_stub.h" + +#include "ecmascript/global_dictionary-inl.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/js_proxy.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_typed_array.h" +#include "ecmascript/object_factory-inl.h" +#include "ecmascript/tagged_dictionary.h" + +namespace panda::ecmascript { +JSTaggedValue FastRuntimeStub::FastAdd(JSTaggedValue left, JSTaggedValue right) +{ + if (left.IsNumber() && right.IsNumber()) { + return JSTaggedValue(left.GetNumber() + right.GetNumber()); + } + + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FastSub(JSTaggedValue left, JSTaggedValue right) +{ + if (left.IsNumber() && right.IsNumber()) { + return JSTaggedValue(left.GetNumber() - right.GetNumber()); + } + + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FastMul(JSTaggedValue left, JSTaggedValue right) +{ + if (left.IsNumber() && right.IsNumber()) { + return JSTaggedValue(left.GetNumber() * right.GetNumber()); + } + + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FastDiv(JSTaggedValue left, JSTaggedValue right) +{ + if (left.IsNumber() && right.IsNumber()) { + double dLeft = left.IsInt() ? left.GetInt() : left.GetDouble(); + double dRight = right.IsInt() ? right.GetInt() : right.GetDouble(); + if (UNLIKELY(dRight == 0.0)) { + if (dLeft == 0.0 || std::isnan(dLeft)) { + return JSTaggedValue(base::NAN_VALUE); + } + uint64_t flagBit = ((bit_cast(dLeft)) ^ (bit_cast(dRight))) & base::DOUBLE_SIGN_MASK; + return JSTaggedValue(bit_cast(flagBit ^ (bit_cast(base::POSITIVE_INFINITY)))); + } + return JSTaggedValue(dLeft / dRight); + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FastMod(JSTaggedValue left, JSTaggedValue right) +{ + if (right.IsInt() && left.IsInt()) { + int iRight = right.GetInt(); + int iLeft = left.GetInt(); + if (iRight > 0 && iLeft > 0) { + return JSTaggedValue(iLeft % iRight); + } + } + if (left.IsNumber() && right.IsNumber()) { + double dLeft = left.IsInt() ? left.GetInt() : left.GetDouble(); + double dRight = right.IsInt() ? right.GetInt() : right.GetDouble(); + if (dRight == 0.0 || std::isnan(dRight) || std::isnan(dLeft) || std::isinf(dLeft)) { + return JSTaggedValue(base::NAN_VALUE); + } + if (dLeft == 0.0 || std::isinf(dRight)) { + return JSTaggedValue(dLeft); + } + return JSTaggedValue(std::fmod(dLeft, dRight)); + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FastEqual(JSTaggedValue left, JSTaggedValue right) +{ + if (left == right) { + if (UNLIKELY(left.IsDouble())) { + return JSTaggedValue(!std::isnan(left.GetDouble())); + } + return JSTaggedValue::True(); + } + if (left.IsNumber()) { + if (left.IsInt() && right.IsInt()) { + // left != right + return JSTaggedValue::False(); + } + } + if (right.IsUndefinedOrNull()) { + if (left.IsHeapObject()) { + return JSTaggedValue::False(); + } + if (left.IsUndefinedOrNull()) { + return JSTaggedValue::True(); + } + } + if (left.IsBoolean()) { + if (right.IsSpecial()) { + return JSTaggedValue::False(); + } + } + return JSTaggedValue::Hole(); +} + +bool FastRuntimeStub::FastStrictEqual(JSTaggedValue left, JSTaggedValue right) +{ + if (left.IsNumber()) { + if (right.IsNumber()) { + double dLeft = left.IsInt() ? left.GetInt() : left.GetDouble(); + double dRight = right.IsInt() ? right.GetInt() : right.GetDouble(); + return JSTaggedValue::StrictNumberEquals(dLeft, dRight); + } + return false; + } + if (right.IsNumber()) { + return false; + } + if (left == right) { + return true; + } + if (left.IsString() && right.IsString()) { + return EcmaString::StringsAreEqual(static_cast(left.GetTaggedObject()), + static_cast(right.GetTaggedObject())); + } + return false; +} + +bool FastRuntimeStub::IsSpecialIndexedObj(JSType jsType) +{ + return jsType > JSType::JS_ARRAY; +} + +bool FastRuntimeStub::IsSpecialReceiverObj(JSType jsType) +{ + return jsType > JSType::JS_PRIMITIVE_REF; +} + +int32_t FastRuntimeStub::TryToElementsIndex(JSTaggedValue key) +{ + if (LIKELY(key.IsInt())) { + return key.GetInt(); + } + if (key.IsString()) { + uint32_t index = 0; + if (JSTaggedValue::StringToElementIndex(key, &index)) { + return static_cast(index); + } + } else if (key.IsDouble()) { + double number = key.GetDouble(); + auto integer = static_cast(number); + if (number == integer) { + return integer; + } + } + return -1; +} + +JSTaggedValue FastRuntimeStub::CallGetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, + JSTaggedValue value) +{ + // Accessor + [[maybe_unused]] EcmaHandleScope handleScope(thread); + AccessorData *accessor = AccessorData::Cast(value.GetTaggedObject()); + if (UNLIKELY(accessor->IsInternal())) { + JSHandle objHandle(thread, holder); + return accessor->CallInternalGet(thread, objHandle); + } + JSHandle objHandle(thread, receiver); + return JSObject::CallGetter(thread, accessor, objHandle); +} + +JSTaggedValue FastRuntimeStub::CallSetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue value, + JSTaggedValue accessorValue) +{ + // Accessor + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle objHandle(thread, receiver); + JSHandle valueHandle(thread, value); + + auto accessor = AccessorData::Cast(accessorValue.GetTaggedObject()); + bool success = JSObject::CallSetter(thread, *accessor, objHandle, valueHandle, true); + return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); +} + +bool FastRuntimeStub::ShouldCallSetter(JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue accessorValue, + PropertyAttributes attr) +{ + if (!AccessorData::Cast(accessorValue.GetTaggedObject())->IsInternal()) { + return true; + } + if (receiver != holder) { + return false; + } + return attr.IsWritable(); +} + +JSTaggedValue FastRuntimeStub::AddPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", JSTaggedValue::Exception()); + } + + JSHandle objHandle(thread, receiver); + JSHandle keyHandle(thread, key); + JSHandle valueHandle(thread, value); + if (objHandle->IsJSArray() && keyHandle.GetTaggedValue() == thread->GlobalConstants()->GetConstructorString()) { + objHandle->GetJSHClass()->SetHasConstructor(true); + } + PropertyAttributes attr = PropertyAttributes::Default(); + uint32_t unusedInlinedProps = objHandle->GetJSHClass()->GetUnusedInlinedProps(); + if (unusedInlinedProps != 0) { + uint32_t order = JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS - unusedInlinedProps; + objHandle->SetPropertyInlinedProps(thread, order, value); + attr.SetOffset(order); + attr.SetIsInlinedProps(true); + JSHClass::AddProperty(thread, objHandle, keyHandle, attr); + return JSTaggedValue::Undefined(); + } + + JSMutableHandle array(thread, objHandle->GetProperties()); + array_size_t length = array->GetLength(); + if (length == 0) { + length = JSObject::MIN_PROPERTIES_LENGTH; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + array.Update(factory->NewTaggedArray(length).GetTaggedValue()); + objHandle->SetProperties(thread, array.GetTaggedValue()); + } + + if (!array->IsDictionaryMode()) { + attr.SetIsInlinedProps(false); + + array_size_t outProps = + JSHClass::DEFAULT_CAPACITY_OF_OUT_OBJECTS - objHandle->GetJSHClass()->GetUnusedNonInlinedProps(); + ASSERT(length >= outProps); + // if array is full, grow array or change to dictionary mode + if (length == outProps) { + if (UNLIKELY(length >= JSHClass::DEFAULT_CAPACITY_OF_OUT_OBJECTS)) { + // change to dictionary and add one. + JSHandle dict(JSObject::TransitionToDictionary(thread, objHandle)); + attr.SetDictionaryOrder(PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES); + auto result = JSTaggedValue(NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr)); + objHandle->SetProperties(thread, result); + // index is not essential when fastMode is false; + return JSTaggedValue::Undefined(); + } + // Grow properties array size + array_size_t capacity = JSObject::ComputePropertyCapacity(length); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + array.Update(factory->CopyArray(array, length, capacity).GetTaggedValue()); + objHandle->SetProperties(thread, array.GetTaggedValue()); + } + + attr.SetOffset(outProps + JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + JSHClass::AddProperty(thread, objHandle, keyHandle, attr); + array->Set(thread, outProps, valueHandle.GetTaggedValue()); + } else { + JSHandle dictHandle(array); + auto result = JSTaggedValue(NameDictionary::PutIfAbsent(thread, dictHandle, keyHandle, valueHandle, attr)); + objHandle->SetProperties(thread, result); + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue FastRuntimeStub::AddPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", JSTaggedValue::Exception()); + } + + bool success = JSObject::AddElementInternal(thread, JSHandle(thread, receiver), index, + JSHandle(thread, value), PropertyAttributes::Default()); + return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); +} + +template +JSTaggedValue FastRuntimeStub::GetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSTaggedValue holder = receiver; + do { + auto *hclass = holder.GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (IsSpecialIndexedObj(jsType)) { + return JSTaggedValue::Hole(); + } + TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(holder)->GetElements().GetTaggedObject()); + + if (!hclass->IsDictionaryElement()) { + ASSERT(!elements->IsDictionaryMode()); + if (index < elements->GetLength()) { + JSTaggedValue value = elements->Get(index); + if (!value.IsHole()) { + return value; + } + } else { + return JSTaggedValue::Hole(); + } + } else { + NumberDictionary *dict = NumberDictionary::Cast(elements); + int entry = dict->FindEntry(JSTaggedValue(static_cast(index))); + if (entry != -1) { + auto attr = dict->GetAttributes(entry); + auto value = dict->GetValue(entry); + if (UNLIKELY(attr.IsAccessor())) { + return CallGetter(thread, receiver, holder, value); + } + ASSERT(!value.IsAccessor()); + return value; + } + } + if (UseOwn) { + break; + } + holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + } while (holder.IsHeapObject()); + + // not found + return JSTaggedValue::Undefined(); +} + +template +JSTaggedValue FastRuntimeStub::GetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) { + return JSTaggedValue::Hole(); + } + // fast path + auto index = TryToElementsIndex(key); + if (LIKELY(index >= 0)) { + return GetPropertyByIndex(thread, receiver, index); + } + if (!key.IsNumber()) { + if (key.IsString() && !EcmaString::Cast(key.GetTaggedObject())->IsInternString()) { + // update string stable + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle receiverHandler(thread, receiver); + key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle(thread, key))); + // Maybe moved by GC + receiver = receiverHandler.GetTaggedValue(); + } + return FastRuntimeStub::GetPropertyByName(thread, receiver, key); + } + return JSTaggedValue::Hole(); +} + +template +JSTaggedValue FastRuntimeStub::GetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + // no gc when return hole + ASSERT(key.IsStringOrSymbol()); + JSTaggedValue holder = receiver; + do { + auto *hclass = holder.GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (IsSpecialIndexedObj(jsType)) { + return JSTaggedValue::Hole(); + } + + if (LIKELY(!hclass->IsDictionaryMode())) { + ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode()); + + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetAttributes().GetTaggedObject()); + + int propsNumber = hclass->GetPropertiesNumber(); + int entry = layoutInfo->FindElementWithCache(thread, hclass, key, propsNumber); + if (entry != -1) { + PropertyAttributes attr(layoutInfo->GetAttr(entry)); + ASSERT(static_cast(attr.GetOffset()) == entry); + auto value = JSObject::Cast(holder)->GetProperty(hclass, attr); + if (UNLIKELY(attr.IsAccessor())) { + return CallGetter(thread, receiver, holder, value); + } + ASSERT(!value.IsAccessor()); + return value; + } + } else { + TaggedArray *array = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject()); + ASSERT(array->IsDictionaryMode()); + NameDictionary *dict = NameDictionary::Cast(array); + int entry = dict->FindEntry(key); + if (entry != -1) { + auto value = dict->GetValue(entry); + auto attr = dict->GetAttributes(entry); + if (UNLIKELY(attr.IsAccessor())) { + return CallGetter(thread, receiver, holder, value); + } + ASSERT(!value.IsAccessor()); + return value; + } + } + if (UseOwn) { + break; + } + holder = hclass->GetPrototype(); + } while (holder.IsHeapObject()); + // not found + return JSTaggedValue::Undefined(); +} + +template +JSTaggedValue FastRuntimeStub::SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value) +{ + // property + JSTaggedValue holder = receiver; + do { + auto *hclass = holder.GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (IsSpecialIndexedObj(jsType)) { + return JSTaggedValue::Hole(); + } + // UpdateRepresentation + if (LIKELY(!hclass->IsDictionaryMode())) { + ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode()); + + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetAttributes().GetTaggedObject()); + + int propsNumber = hclass->GetPropertiesNumber(); + int entry = layoutInfo->FindElementWithCache(thread, hclass, key, propsNumber); + if (entry != -1) { + PropertyAttributes attr(layoutInfo->GetAttr(entry)); + ASSERT(static_cast(attr.GetOffset()) == entry); + if (UNLIKELY(attr.IsAccessor())) { + auto accessor = JSObject::Cast(holder)->GetProperty(hclass, attr); + if (ShouldCallSetter(receiver, holder, accessor, attr)) { + return CallSetter(thread, receiver, value, accessor); + } + } + if (UNLIKELY(!attr.IsWritable())) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set readonly property", JSTaggedValue::Exception()); + } + if (UNLIKELY(holder != receiver)) { + break; + } + JSObject::Cast(holder)->SetProperty(thread, hclass, attr, value); + return JSTaggedValue::Undefined(); + } + } else { + TaggedArray *properties = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject()); + ASSERT(properties->IsDictionaryMode()); + NameDictionary *dict = NameDictionary::Cast(properties); + int entry = dict->FindEntry(key); + if (entry != -1) { + auto attr = dict->GetAttributes(entry); + if (UNLIKELY(attr.IsAccessor())) { + auto accessor = dict->GetValue(entry); + if (ShouldCallSetter(receiver, holder, accessor, attr)) { + return CallSetter(thread, receiver, value, accessor); + } + } + if (UNLIKELY(!attr.IsWritable())) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set readonly property", JSTaggedValue::Exception()); + } + if (UNLIKELY(holder != receiver)) { + break; + } + dict->UpdateValue(thread, entry, value); + return JSTaggedValue::Undefined(); + } + } + if (UseOwn) { + break; + } + holder = hclass->GetPrototype(); + } while (holder.IsHeapObject()); + + return AddPropertyByName(thread, receiver, key, value); +} + +template +JSTaggedValue FastRuntimeStub::SetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value) +{ + JSTaggedValue holder = receiver; + do { + auto *hclass = holder.GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (IsSpecialIndexedObj(jsType)) { + return JSTaggedValue::Hole(); + } + TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(holder)->GetElements().GetTaggedObject()); + if (!hclass->IsDictionaryElement()) { + ASSERT(!elements->IsDictionaryMode()); + if (UNLIKELY(holder != receiver)) { + break; + } + if (index < elements->GetLength()) { + if (!elements->Get(index).IsHole()) { + elements->Set(thread, index, value); + return JSTaggedValue::Undefined(); + } + } + } else { + return JSTaggedValue::Hole(); + } + if (UseOwn) { + break; + } + holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + } while (holder.IsHeapObject()); + + return AddPropertyByIndex(thread, receiver, index, value); +} + +template +JSTaggedValue FastRuntimeStub::SetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value) +{ + if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) { + return JSTaggedValue::Hole(); + } + // fast path + auto index = TryToElementsIndex(key); + if (LIKELY(index >= 0)) { + return SetPropertyByIndex(thread, receiver, index, value); + } + if (!key.IsNumber()) { + if (key.IsString() && !EcmaString::Cast(key.GetTaggedObject())->IsInternString()) { + // update string stable + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle receiverHandler(thread, receiver); + JSHandle valueHandler(thread, value); + key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle(thread, key))); + // Maybe moved by GC + receiver = receiverHandler.GetTaggedValue(); + value = valueHandler.GetTaggedValue(); + } + return FastRuntimeStub::SetPropertyByName(thread, receiver, key, value); + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::GetGlobalOwnProperty(JSTaggedValue receiver, JSTaggedValue key, bool *found) +{ + JSObject *obj = JSObject::Cast(receiver); + TaggedArray *properties = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + GlobalDictionary *dict = GlobalDictionary::Cast(properties); + int entry = dict->FindEntry(key); + if (entry != -1) { + *found = true; + return dict->GetValue(entry); + } + + return JSTaggedValue::Undefined(); +} + +JSTaggedValue FastRuntimeStub::FastTypeOf(JSThread *thread, JSTaggedValue obj) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + switch (obj.GetRawData()) { + case JSTaggedValue::VALUE_TRUE: + case JSTaggedValue::VALUE_FALSE: + return globalConst->GetBooleanString(); + case JSTaggedValue::VALUE_NULL: + return globalConst->GetObjectString(); + case JSTaggedValue::VALUE_UNDEFINED: + return globalConst->GetUndefinedString(); + default: + if (obj.IsHeapObject()) { + if (obj.IsString()) { + return globalConst->GetStringString(); + } + if (obj.IsSymbol()) { + return globalConst->GetSymbolString(); + } + if (obj.IsCallable()) { + return globalConst->GetFunctionString(); + } + return globalConst->GetObjectString(); + } + if (obj.IsNumber()) { + return globalConst->GetNumberString(); + } + } + return globalConst->GetUndefinedString(); +} + +bool FastRuntimeStub::FastSetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value) +{ + JSTaggedValue result = FastRuntimeStub::SetPropertyByIndex(thread, receiver, index, value); + if (!result.IsHole()) { + return result != JSTaggedValue::Exception(); + } + return JSTaggedValue::SetProperty(thread, JSHandle(thread, receiver), index, + JSHandle(thread, value), true); +} + +bool FastRuntimeStub::FastSetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value) +{ + JSTaggedValue result = FastRuntimeStub::SetPropertyByValue(thread, receiver, key, value); + if (!result.IsHole()) { + return result != JSTaggedValue::Exception(); + } + return JSTaggedValue::SetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key), JSHandle(thread, value), + true); +} + +// must not use for interpreter +JSTaggedValue FastRuntimeStub::FastGetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + ASSERT(key.IsStringOrSymbol()); + if (key.IsString() && !EcmaString::Cast(key.GetTaggedObject())->IsInternString()) { + JSHandle receiverHandler(thread, receiver); + key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle(thread, key))); + // Maybe moved by GC + receiver = receiverHandler.GetTaggedValue(); + } + JSTaggedValue result = FastRuntimeStub::GetPropertyByName(thread, receiver, key); + if (result.IsHole()) { + return JSTaggedValue::GetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key)) + .GetValue() + .GetTaggedValue(); + } + return result; +} + +JSTaggedValue FastRuntimeStub::FastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + JSTaggedValue result = FastRuntimeStub::GetPropertyByValue(thread, receiver, key); + if (result.IsHole()) { + return JSTaggedValue::GetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key)) + .GetValue() + .GetTaggedValue(); + } + return result; +} + +template // UseHole is only for Array::Sort() which requires Hole order +JSTaggedValue FastRuntimeStub::FastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index) +{ + JSTaggedValue result = GetPropertyByIndex(thread, receiver, index); + if (result.IsHole() && !UseHole) { + return JSTaggedValue::GetProperty(thread, JSHandle(thread, receiver), index) + .GetValue() + .GetTaggedValue(); + } + return result; +} + +JSTaggedValue FastRuntimeStub::NewLexicalEnvDyn(JSThread *thread, ObjectFactory *factory, uint16_t numVars) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + LexicalEnv *newEnv = factory->InlineNewLexicalEnv(numVars); + if (UNLIKELY(newEnv == nullptr)) { + return JSTaggedValue::Hole(); + } + JSTaggedValue currentLexenv = thread->GetCurrentLexenv(); + newEnv->SetParentEnv(thread, currentLexenv); + return JSTaggedValue(newEnv); +} + +// Those interface below is discarded +bool FastRuntimeStub::IsSpecialIndexedObjForGet(JSTaggedValue obj) +{ + JSType jsType = obj.GetHeapObject()->ClassAddr()->GetObjectType(); + return jsType > JSType::JS_ARRAY && jsType <= JSType::JS_PRIMITIVE_REF; +} + +bool FastRuntimeStub::IsSpecialIndexedObjForSet(JSTaggedValue obj) +{ + JSType jsType = obj.GetHeapObject()->ClassAddr()->GetObjectType(); + return jsType >= JSType::JS_ARRAY && jsType <= JSType::JS_PRIMITIVE_REF; +} + +JSTaggedValue FastRuntimeStub::GetElement(JSTaggedValue receiver, uint32_t index) +{ + JSTaggedValue holder = receiver; + while (true) { + JSTaggedValue val = FindOwnElement(JSObject::Cast(holder), index); + if (!val.IsHole()) { + return val; + } + + holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + if (!holder.IsHeapObject()) { + return JSTaggedValue::Undefined(); + } + } +} + +JSTaggedValue FastRuntimeStub::GetElementWithArray(JSTaggedValue receiver, uint32_t index) +{ + DISALLOW_GARBAGE_COLLECTION; + JSTaggedValue holder = receiver; + while (true) { + JSTaggedValue val = FindOwnElement(JSObject::Cast(holder), index); + if (!val.IsHole()) { + return val; + } + + holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + if (!holder.IsHeapObject()) { + return val; + } + } +} + +bool FastRuntimeStub::SetElement(JSThread *thread, JSTaggedValue receiver, uint32_t index, JSTaggedValue value, + bool mayThrow) +{ + JSTaggedValue holder = receiver; + bool onPrototype = false; + + while (true) { + PropertyAttributes attr; + uint32_t indexOrEntry = 0; + TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(holder)->GetElements().GetHeapObject()); + bool isDict = elements->IsDictionaryMode(); + JSTaggedValue val = FindOwnElement(elements, index, isDict, &attr, &indexOrEntry); + if (!val.IsHole()) { + if (UNLIKELY(onPrototype)) { + if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", false); + } + return false; + } + + return JSObject::AddElementInternal(thread, JSHandle(thread, receiver), index, + JSHandle(thread, value), + PropertyAttributes::Default()); + } + if (!attr.IsAccessor()) { + if (attr.IsWritable()) { + elements = TaggedArray::Cast(JSObject::Cast(receiver)->GetElements().GetHeapObject()); + if (!isDict) { + elements->Set(thread, indexOrEntry, value); + JSObject::Cast(receiver)->GetJSHClass()->UpdateRepresentation(value); + return true; + } + NumberDictionary::Cast(elements)->UpdateValueAndAttributes(thread, indexOrEntry, value, attr); + return true; + } + + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set readonly property", false); + } + return false; + } + + // Accessor + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle objHandle(thread, receiver); + JSHandle valueHandle(thread, value); + AccessorData *access = AccessorData::Cast(val.GetHeapObject()); + return JSObject::CallSetter(thread, *access, objHandle, valueHandle, mayThrow); + } + + holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + if (!holder.IsHeapObject()) { + if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", false); + } + return false; + } + + return JSObject::AddElementInternal(thread, JSHandle(thread, receiver), index, + JSHandle(thread, value), PropertyAttributes::Default()); + } + if (holder.IsJSProxy()) { + return JSProxy::SetProperty( + thread, JSHandle(thread, holder), JSHandle(thread, JSTaggedValue(index)), + JSHandle(thread, value), JSHandle(thread, receiver), mayThrow); + } + onPrototype = true; + } +} + +bool FastRuntimeStub::SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value, bool mayThrow) +{ + // property + JSTaggedValue holder = receiver; + bool onPrototype = false; + + while (true) { + TaggedArray *properties = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetHeapObject()); + PropertyAttributes attr; + uint32_t indexOrEntry = 0; + JSTaggedValue val = FindOwnProperty(thread, JSObject::Cast(holder), properties, key, &attr, &indexOrEntry); + if (!val.IsHole()) { + if (!attr.IsAccessor()) { + if (UNLIKELY(onPrototype)) { + if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible() || !attr.IsWritable())) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", false); + } + return false; + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectOperator::FastAdd(thread, receiver, key, JSHandle(thread, value), + PropertyAttributes::Default()); + + return true; + } + + if (attr.IsWritable()) { + properties = TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetHeapObject()); + if (!properties->IsDictionaryMode()) { + Representation representation = + PropertyAttributes::UpdateRepresentation(attr.GetRepresentation(), value); + if (attr.GetRepresentation() != representation) { + attr.SetRepresentation(representation); + } + + JSObject::Cast(receiver)->GetJSHClass()->UpdatePropertyMetaData(thread, key, attr); + if (UNLIKELY(val.IsInternalAccessor())) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + AccessorData::Cast(val.GetHeapObject()) + ->CallInternalSet(thread, JSHandle(thread, receiver), + JSHandle(thread, value), mayThrow); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return true; + } + + if (attr.IsInlinedProps()) { + JSObject::Cast(receiver)->SetPropertyInlinedProps(thread, indexOrEntry, value); + } else { + properties->Set(thread, indexOrEntry, value); + } + return true; + } + + if (receiver.IsJSGlobalObject()) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle dict_handle(thread, properties); + // globalobj have no internal accessor + GlobalDictionary::InvalidatePropertyBox(thread, dict_handle, indexOrEntry, attr); + return true; + } + + if (UNLIKELY(val.IsInternalAccessor())) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + AccessorData::Cast(val.GetHeapObject()) + ->CallInternalSet(thread, JSHandle(thread, receiver), + JSHandle(thread, value), mayThrow); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return true; + } + + NameDictionary::Cast(properties)->UpdateValueAndAttributes(thread, indexOrEntry, value, attr); + return true; + } + + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set readonly property", false); + } + return false; + } + + // Accessor + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle objHandle(thread, receiver); + JSHandle valueHandle(thread, value); + AccessorData *access = AccessorData::Cast(val.GetHeapObject()); + return JSObject::CallSetter(thread, *access, objHandle, valueHandle, mayThrow); + } + + if (holder.IsTypedArray()) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + return JSTypedArray::SetProperty(thread, JSHandle(thread, holder), + JSTypedArray::ToPropKey(thread, JSHandle(thread, key)), + JSHandle(thread, value), + JSHandle(thread, receiver), mayThrow); + } + + holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + if (!holder.IsHeapObject()) { + if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", false); + } + return false; + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectOperator::FastAdd(thread, receiver, key, JSHandle(thread, value), + PropertyAttributes::Default()); + + return true; + } + if (holder.IsJSProxy()) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + return JSProxy::SetProperty(thread, JSHandle(thread, holder), JSHandle(thread, key), + JSHandle(thread, value), + JSHandle(thread, receiver), mayThrow); + } + onPrototype = true; + } +} + +bool FastRuntimeStub::SetGlobalOwnProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value, bool mayThrow) +{ + uint32_t index = 0; + if (JSTaggedValue::ToElementIndex(key, &index)) { + return SetElement(thread, receiver, index, value, mayThrow); + } + + JSObject *obj = JSObject::Cast(receiver); + GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject()); + PropertyAttributes attr = PropertyAttributes::Default(); + if (UNLIKELY(dict->GetLength() == 0)) { + JSHandle keyHandle(thread, key); + JSHandle valHandle(thread, value); + JSHandle objHandle(thread, obj); + JSHandle dict_handle(thread, GlobalDictionary::Create(thread)); + + // Add PropertyBox to global dictionary + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle boxHandle = factory->NewPropertyBox(valHandle); + boxHandle->SetValue(thread, valHandle.GetTaggedValue()); + PropertyBoxType boxType = valHandle->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT; + attr.SetBoxType(boxType); + + objHandle->SetProperties( + thread, JSTaggedValue(GlobalDictionary::PutIfAbsent(thread, dict_handle, keyHandle, + JSHandle(boxHandle), attr))); + return true; + } + + int entry = dict->FindEntry(key); + if (entry != -1) { + attr = dict->GetAttributes(entry); + JSTaggedValue val = dict->GetValue(entry); + if (!attr.IsAccessor()) { + if (attr.IsWritable()) { + // globalobj have no internal accessor + JSHandle dict_handle(thread, dict); + GlobalDictionary::InvalidatePropertyBox(thread, dict_handle, entry, attr); + return true; + } + } + + // Accessor + JSTaggedValue setter = AccessorData::Cast(val.GetHeapObject())->GetSetter(); + if (setter.IsUndefined()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property when setter is undefined", false); + } + return false; + } + + JSHandle objHandle(thread, receiver); + JSHandle valueHandle(thread, value); + JSHandle setFunc(thread, setter); + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1); + args->Set(thread, 0, valueHandle); + JSFunction::Call(thread, setFunc, objHandle, args); + // 10. ReturnIfAbrupt(setterResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return true; + } + + JSHandle keyHandle(thread, key); + JSHandle valHandle(thread, value); + JSHandle objHandle(thread, obj); + JSHandle dict_handle(thread, dict); + + // Add PropertyBox to global dictionary + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle boxHandle = factory->NewPropertyBox(keyHandle); + boxHandle->SetValue(thread, valHandle.GetTaggedValue()); + PropertyBoxType boxType = valHandle->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT; + attr.SetBoxType(boxType); + + objHandle->SetProperties(thread, JSTaggedValue(GlobalDictionary::PutIfAbsent( + thread, dict_handle, keyHandle, + JSHandle(boxHandle), attr))); + return true; +} + +// set property that is not accessor and is writable +void FastRuntimeStub::SetOwnPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value) +{ + TaggedArray *properties = TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetHeapObject()); + PropertyAttributes attr; + uint32_t indexOrEntry; + JSTaggedValue val = FindOwnProperty(thread, JSObject::Cast(receiver), properties, key, &attr, &indexOrEntry); + if (!val.IsHole()) { + ASSERT(!attr.IsAccessor() && attr.IsWritable()); + if (!properties->IsDictionaryMode()) { + Representation representation = PropertyAttributes::UpdateRepresentation(attr.GetRepresentation(), value); + if (attr.GetRepresentation() != representation) { + attr.SetRepresentation(representation); + } + + JSObject::Cast(receiver)->GetJSHClass()->UpdatePropertyMetaData(thread, key, attr); + + if (attr.IsInlinedProps()) { + JSObject::Cast(receiver)->SetPropertyInlinedProps(thread, indexOrEntry, value); + } else { + properties->Set(thread, indexOrEntry, value); + } + return; + } + + NameDictionary::Cast(properties)->UpdateValueAndAttributes(thread, indexOrEntry, value, attr); + return; + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectOperator::FastAdd(thread, receiver, key, JSHandle(thread, value), + PropertyAttributes::Default()); +} + +// set element that is not accessor and is writable +bool FastRuntimeStub::SetOwnElement(JSThread *thread, JSTaggedValue receiver, uint32_t index, JSTaggedValue value) +{ + PropertyAttributes attr; + uint32_t indexOrEntry; + TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(receiver)->GetElements().GetHeapObject()); + bool isDict = elements->IsDictionaryMode(); + [[maybe_unused]] JSTaggedValue val = FindOwnElement(elements, index, isDict, &attr, &indexOrEntry); + if (!val.IsHole()) { + ASSERT(!attr.IsAccessor() && attr.IsWritable()); + if (!isDict) { + elements->Set(thread, indexOrEntry, value); + JSObject::Cast(receiver)->GetJSHClass()->UpdateRepresentation(value); + return true; + } + NumberDictionary::Cast(elements)->UpdateValueAndAttributes(thread, indexOrEntry, value, attr); + return true; + } + + return JSObject::AddElementInternal(thread, JSHandle(thread, receiver), index, + JSHandle(thread, value), PropertyAttributes::Default()); +} + +bool FastRuntimeStub::FastSetProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, JSTaggedValue value, + bool mayThrow) +{ + if (receiver.IsJSObject() && !receiver.IsTypedArray() && (key.IsStringOrSymbol())) { + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key, &index))) { + if (!FastRuntimeStub::IsSpecialIndexedObjForSet(receiver)) { + return FastRuntimeStub::SetElement(thread, receiver, index, value, true); + } + return JSTaggedValue::SetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key), + JSHandle(thread, value), mayThrow); + } + if (key.IsString()) { + key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle(thread, key))); + } + return FastRuntimeStub::SetPropertyByName(thread, receiver, key, value, mayThrow); + } + return JSTaggedValue::SetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key), JSHandle(thread, value), + mayThrow); +} + +JSTaggedValue FastRuntimeStub::FastGetProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + JSTaggedValue result; + if (receiver.IsJSObject() && !receiver.IsTypedArray() && (key.IsStringOrSymbol())) { + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key, &index))) { + if (FastRuntimeStub::IsSpecialIndexedObjForSet(receiver)) { + result = JSTaggedValue::Hole(); + } else { + result = FastRuntimeStub::GetElement(receiver, index); + } + } else { + if (key.IsString()) { + key = JSTaggedValue( + thread->GetEcmaVM()->GetFactory()->InternString(JSHandle(thread, key))); + } + result = FastRuntimeStub::GetPropertyByName(thread, receiver, key); + } + } + if (!result.IsHole()) { + if (UNLIKELY(result.IsAccessor())) { + return JSObject::CallGetter(thread, AccessorData::Cast(result.GetHeapObject()), + JSHandle(thread, receiver)); + } + return result; + } + return JSTaggedValue::GetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key)) + .GetValue() + .GetTaggedValue(); +} + +JSTaggedValue FastRuntimeStub::FindOwnProperty(JSThread *thread, JSObject *obj, TaggedArray *properties, + JSTaggedValue key, PropertyAttributes *attr, uint32_t *indexOrEntry) +{ + if (!properties->IsDictionaryMode()) { + JSHClass *cls = obj->GetJSHClass(); + JSTaggedValue attrs = cls->GetAttributes(); + if (!attrs.IsNull()) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetHeapObject()); + int propNumber = cls->GetPropertiesNumber(); + int entry = layoutInfo->FindElementWithCache(thread, cls, key, propNumber); + if (entry != -1) { + *attr = layoutInfo->GetAttr(entry); + ASSERT(entry == static_cast(attr->GetOffset())); + *indexOrEntry = entry; + if (attr->IsInlinedProps()) { + return obj->GetPropertyInlinedProps(entry); + } + *indexOrEntry -= JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS; + return properties->Get(*indexOrEntry); + } + } + return JSTaggedValue::Hole(); // properties == empty properties will return here. + } + + if (obj->IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(properties); + int entry = dict->FindEntry(key); + if (entry != -1) { + *indexOrEntry = entry; + *attr = dict->GetAttributes(entry); + return dict->GetValue(entry); + } + return JSTaggedValue::Hole(); + } + + NameDictionary *dict = NameDictionary::Cast(properties); + int entry = dict->FindEntry(key); + if (entry != -1) { + *indexOrEntry = entry; + *attr = dict->GetAttributes(entry); + return dict->GetValue(entry); + } + + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FindOwnElement(TaggedArray *elements, uint32_t index, bool isDict, + PropertyAttributes *attr, uint32_t *indexOrEntry) +{ + if (!isDict) { + if (elements->GetLength() <= index) { + return JSTaggedValue::Hole(); + } + + JSTaggedValue value = elements->Get(index); + if (!value.IsHole()) { + *attr = PropertyAttributes::Default(); + *indexOrEntry = index; + return value; + } + } else { + NumberDictionary *dict = NumberDictionary::Cast(elements); + int entry = dict->FindEntry(JSTaggedValue(static_cast(index))); + if (entry != -1) { + *indexOrEntry = entry; + *attr = dict->GetAttributes(entry); + return dict->GetValue(entry); + } + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FindOwnProperty(JSThread *thread, JSObject *obj, JSTaggedValue key) +{ + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetHeapObject()); + if (!array->IsDictionaryMode()) { + JSHClass *cls = obj->GetJSHClass(); + JSTaggedValue attrs = cls->GetAttributes(); + if (!attrs.IsNull()) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetHeapObject()); + int propsNumber = cls->GetPropertiesNumber(); + int entry = layoutInfo->FindElementWithCache(thread, cls, key, propsNumber); + if (entry != -1) { + PropertyAttributes attr(layoutInfo->GetAttr(entry)); + ASSERT(static_cast(attr.GetOffset()) == entry); + return attr.IsInlinedProps() ? obj->GetPropertyInlinedProps(entry) + : array->Get(entry - JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + } + } + return JSTaggedValue::Hole(); // array == empty array will return here. + } + + if (obj->IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(array); + int entry = dict->FindEntry(key); + if (entry != -1) { + return dict->GetValue(entry); + } + return JSTaggedValue::Hole(); + } + + NameDictionary *dict = NameDictionary::Cast(array); + int entry = dict->FindEntry(key); + if (entry != -1) { + return dict->GetValue(entry); + } + + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FindOwnElement(JSObject *obj, uint32_t index) +{ + TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(obj)->GetElements().GetHeapObject()); + + if (!elements->IsDictionaryMode()) { + if (elements->GetLength() <= index) { + return JSTaggedValue::Hole(); + } + + JSTaggedValue value = elements->Get(index); + if (!value.IsHole()) { + return value; + } + } else { + NumberDictionary *dict = NumberDictionary::Cast(elements); + int entry = dict->FindEntry(JSTaggedValue(static_cast(index))); + if (entry != -1) { + return dict->GetValue(entry); + } + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::HasOwnProperty(JSThread *thread, JSObject *obj, JSTaggedValue key) +{ + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key, &index))) { + return FastRuntimeStub::FindOwnElement(obj, index); + } + + return FastRuntimeStub::FindOwnProperty(thread, obj, key); +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_FAST_RUNTIME_STUB_INL_H diff --git a/ecmascript/interpreter/fast_runtime_stub.h b/ecmascript/interpreter/fast_runtime_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..e18362b9aa3b17d7652a0117a6512fbaedccd826 --- /dev/null +++ b/ecmascript/interpreter/fast_runtime_stub.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_FAST_RUNTIME_STUB_H +#define PANDA_RUNTIME_ECMASCRIPT_FAST_RUNTIME_STUB_H + +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class GlobalEnv; +class PropertyAttributes; + +class FastRuntimeStub { +public: + /* -------------- Common API Begin, Don't change those interface!!! ----------------- */ + static inline JSTaggedValue FastAdd(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue FastSub(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue FastMul(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue FastDiv(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue FastMod(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue FastEqual(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue FastTypeOf(JSThread *thread, JSTaggedValue obj); + static inline bool FastStrictEqual(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue NewLexicalEnvDyn(JSThread *thread, ObjectFactory *factory, uint16_t numVars); + static inline JSTaggedValue GetGlobalOwnProperty(JSTaggedValue receiver, JSTaggedValue key, bool *found); + /* -------------- Special API For Multi-Language VM Begin ----------------- */ + static inline bool IsSpecialIndexedObjForGet(JSTaggedValue obj); + static inline bool IsSpecialIndexedObjForSet(JSTaggedValue obj); + static inline JSTaggedValue GetElement(JSTaggedValue receiver, uint32_t index); + static inline JSTaggedValue GetElementWithArray(JSTaggedValue receiver, uint32_t index); + static inline bool SetElement(JSThread *thread, JSTaggedValue receiver, uint32_t index, JSTaggedValue value, + bool mayThrow); + static inline bool SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value, bool mayThrow); + static inline bool SetGlobalOwnProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value, bool mayThrow); + + // set property that is not accessor and is writable + static inline void SetOwnPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value); + // set element that is not accessor and is writable + static inline bool SetOwnElement(JSThread *thread, JSTaggedValue receiver, uint32_t index, JSTaggedValue value); + static inline bool FastSetProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, JSTaggedValue value, + bool mayThrow); + static inline JSTaggedValue FastGetProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); + static inline JSTaggedValue FindOwnProperty(JSThread *thread, JSObject *obj, TaggedArray *properties, + JSTaggedValue key, PropertyAttributes *attr, uint32_t *indexOrEntry); + static inline JSTaggedValue FindOwnElement(TaggedArray *elements, uint32_t index, bool isDict, + PropertyAttributes *attr, uint32_t *indexOrEntry); + static inline JSTaggedValue FindOwnProperty(JSThread *thread, JSObject *obj, JSTaggedValue key); + + static inline JSTaggedValue FindOwnElement(JSObject *obj, uint32_t index); + + static inline JSTaggedValue HasOwnProperty(JSThread *thread, JSObject *obj, JSTaggedValue key); + /* -------------- Special API For Multi-Language VM End ----------------- */ + /* -------------- Common API End, Don't change those interface!!! ----------------- */ + + template + static inline JSTaggedValue GetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); + template + static inline JSTaggedValue GetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); + template + static inline JSTaggedValue GetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index); + template + static inline JSTaggedValue SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value); + template + static inline JSTaggedValue SetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value); + template + static inline JSTaggedValue SetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value); + + static inline bool FastSetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value); + static inline bool FastSetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value); + static inline JSTaggedValue FastGetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); + static inline JSTaggedValue FastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); + template + static inline JSTaggedValue FastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index); + +private: + friend class ICRuntimeStub; + static inline bool IsSpecialIndexedObj(JSType jsType); + static inline bool IsSpecialReceiverObj(JSType jsType); + static inline int32_t TryToElementsIndex(JSTaggedValue key); + static inline JSTaggedValue CallGetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, + JSTaggedValue value); + static inline JSTaggedValue CallSetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue value, + JSTaggedValue accessorValue); + static inline bool ShouldCallSetter(JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue accessorValue, + PropertyAttributes attr); + static inline JSTaggedValue AddPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value); + static inline JSTaggedValue AddPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value); +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_OBJECT_OPERATOR_INL_H diff --git a/ecmascript/interpreter/frame_handler.cpp b/ecmascript/interpreter/frame_handler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c2e68d55e3094c9fe9427db513a9c07dcb22690 --- /dev/null +++ b/ecmascript/interpreter/frame_handler.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "frame_handler.h" +#include "ecmascript/interpreter/interpreter.h" +#include "ecmascript/js_thread.h" +#include "libpandafile/bytecode_instruction-inl.h" + +namespace panda::ecmascript { +EcmaFrameHandler::EcmaFrameHandler(const JSThread *thread) +{ + sp_ = thread->GetCurrentSPFrame(); +} + +bool EcmaFrameHandler::HasFrame() const +{ + if (sp_ == nullptr) { + return false; + } + + // Should not be a break frame + return !IsBreakFrame(); +} + +bool EcmaFrameHandler::IsBreakFrame() const +{ + ASSERT(sp_ != nullptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp_) - 1; + return state->sp == nullptr; +} + +void EcmaFrameHandler::PrevFrame() +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp_) - 1; + sp_ = state->prev; +} + +EcmaFrameHandler EcmaFrameHandler::GetPrevFrame() const +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp_) - 1; + return EcmaFrameHandler(state->prev); +} + +JSTaggedValue EcmaFrameHandler::GetVRegValue(size_t index) const +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return JSTaggedValue(sp_[index]); +} + +void EcmaFrameHandler::SetVRegValue(size_t index, JSTaggedValue value) +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + sp_[index] = value.GetRawData(); +} + +JSTaggedValue EcmaFrameHandler::GetAcc() const +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp_) - 1; + return state->acc; +} + +uint32_t EcmaFrameHandler::GetSize() const +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp_) - 1; + JSTaggedType *prevSp = state->prev; + ASSERT(prevSp != nullptr); + auto size = (prevSp - sp_) - FRAME_STATE_SIZE; + return static_cast(size); +} + +uint32_t EcmaFrameHandler::GetBytecodeOffset() const +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp_) - 1; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto offset = state->pc - JSMethod::Cast(state->method)->GetBytecodeArray(); + return static_cast(offset); +} + +JSMethod *EcmaFrameHandler::GetMethod() const +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp_) - 1; + return state->method; +} + +JSTaggedValue EcmaFrameHandler::GetFunction() const +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp_) - 1; + uint32_t numVregs = state->method->GetNumVregs(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto func = JSTaggedValue(sp_[numVregs]); + return func; +} + +const uint8_t *EcmaFrameHandler::GetPc() const +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp_) - 1; + return state->pc; +} + +JSTaggedType *EcmaFrameHandler::GetSp() const +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp_) - 1; + return state->sp; +} + +ConstantPool *EcmaFrameHandler::GetConstpool() const +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp_) - 1; + return state->constpool; +} + +JSTaggedValue EcmaFrameHandler::GetEnv() const +{ + ASSERT(HasFrame()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp_) - 1; + return state->env; +} + +void EcmaFrameHandler::Iterate(const RootVisitor &v0, const RootRangeVisitor &v1) +{ + JSTaggedType *current = sp_; + while (current != nullptr) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(current) - 1; + + if (state->sp != nullptr) { + uintptr_t start = ToUintPtr(current); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *prev_state = reinterpret_cast(state->prev) - 1; + uintptr_t end = ToUintPtr(prev_state); + v1(Root::ROOT_FRAME, ObjectSlot(start), ObjectSlot(end)); + if (state->pc != nullptr) { + // interpreter frame + v0(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&state->acc))); + v0(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&state->constpool))); + v0(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&state->env))); + v0(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(&state->profileTypeInfo))); + } + } + current = state->prev; + } +} + +void EcmaFrameHandler::DumpStack(std::ostream &os) const +{ + size_t i = 0; + EcmaFrameHandler frameHandler(sp_); + for (; frameHandler.HasFrame(); frameHandler.PrevFrame()) { + os << "[" << i++ + << "]:" << frameHandler.GetMethod()->GetStringDataAnnotation(Method::AnnotationField::FUNCTION_NAME).data + << "\n"; + } +} + +void EcmaFrameHandler::DumpPC(std::ostream &os, const uint8_t *pc) const +{ + EcmaFrameHandler frameHandler(sp_); + ASSERT(frameHandler.HasFrame()); + + // NOLINTNEXTLINE(cppcoreguidelines-narrowing-conversions, bugprone-narrowing-conversions) + int offset = pc - JSMethod::Cast(frameHandler.GetMethod())->GetBytecodeArray(); + os << "offset: " << offset << "\n"; +} +} // namespace panda::ecmascript diff --git a/ecmascript/interpreter/frame_handler.h b/ecmascript/interpreter/frame_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..bea45197e3ba9fe6d4e71c1eba608d56346577f2 --- /dev/null +++ b/ecmascript/interpreter/frame_handler.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_FRAME_HANDLER_H +#define PANDA_RUNTIME_ECMASCRIPT_FRAME_HANDLER_H + +#include "ecmascript/js_method.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/mem/heap_roots.h" + +namespace panda { +namespace ecmascript { +class JSThread; +class JSFunction; +class ConstantPool; + +class EcmaFrameHandler { +public: + explicit EcmaFrameHandler(JSTaggedType *sp) : sp_(sp) {} + explicit EcmaFrameHandler(const JSThread *thread); + ~EcmaFrameHandler() = default; + DEFAULT_COPY_SEMANTIC(EcmaFrameHandler); + DEFAULT_MOVE_SEMANTIC(EcmaFrameHandler); + + bool HasFrame() const; + bool IsBreakFrame() const; + void PrevFrame(); + EcmaFrameHandler GetPrevFrame() const; + + JSTaggedValue GetVRegValue(size_t index) const; + void SetVRegValue(size_t index, JSTaggedValue value); + + JSTaggedValue GetAcc() const; + uint32_t GetSize() const; + uint32_t GetBytecodeOffset() const; + JSMethod *GetMethod() const; + JSTaggedValue GetFunction() const; + const uint8_t *GetPc() const; + JSTaggedType *GetSp() const; + ConstantPool *GetConstpool() const; + JSTaggedValue GetEnv() const; + + void Iterate(const RootVisitor &v0, const RootRangeVisitor &v1); + + void DumpStack(std::ostream &os) const; + void DumpStack() const + { + DumpStack(std::cout); + } + void DumpPC(std::ostream &os, const uint8_t *pc) const; + void DumpPC(const uint8_t *pc) const + { + DumpPC(std::cout, pc); + } + +private: + JSTaggedType *sp_{nullptr}; +}; +} // namespace ecmascript +} // namespace panda +#endif // PANDA_RUNTIME_ECMASCRIPT_FRAME_HANDLER_H diff --git a/ecmascript/interpreter/interpreter-inl.h b/ecmascript/interpreter/interpreter-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..aec2acf5d2cd51be1408b73960b7292390de83b9 --- /dev/null +++ b/ecmascript/interpreter/interpreter-inl.h @@ -0,0 +1,3117 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_INTERPRETER_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_INTERPRETER_INL_H + +#include "ecmascript/class_linker/program_object-inl.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/ic/fast_ic_runtime_stub-inl.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/interpreter/interpreter.h" +#include "ecmascript/interpreter/slow_runtime_stub.h" +#include "ecmascript/js_generator_object.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/literal_data_extractor.h" +#include "ecmascript/runtime_call_id.h" +#include "ecmascript/template_string.h" +#include "ecmascript/vmstat/runtime_stat.h" +#include "include/runtime_notification.h" +#include "libpandafile/code_data_accessor.h" +#include "libpandafile/file.h" +#include "libpandafile/method_data_accessor.h" + +namespace panda::ecmascript { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wvoid-ptr-dereference" +#pragma clang diagnostic ignored "-Wgnu-label-as-value" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#endif + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define LOG_INST() LOG(DEBUG, INTERPRETER) << ": " + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define HANDLE_OPCODE(handle_opcode) \ + handle_opcode: // NOLINT(clang-diagnostic-gnu-label-as-value, cppcoreguidelines-macro-usage) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ADVANCE_PC(offset) \ + pc += (offset); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-macro-usage) + +#define GOTO_NEXT() // NOLINT(clang-diagnostic-gnu-label-as-value, cppcoreguidelines-macro-usage) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DISPATCH(format) \ + do { \ + ADVANCE_PC(BytecodeInstruction::Size(format)) \ + opcode = READ_INST_OP(); goto * dispatchTable[opcode]; \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DISPATCH_OFFSET(offset) \ + do { \ + ADVANCE_PC(offset) \ + opcode = READ_INST_OP(); goto * dispatchTable[opcode]; \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GET_FRAME(CurrentSp) \ + (reinterpret_cast(CurrentSp) - 1) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SAVE_PC() (GET_FRAME(sp)->pc = pc) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SAVE_ACC() (GET_FRAME(sp)->acc = acc) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RESTORE_ACC() (acc = GET_FRAME(sp)->acc) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTERPRETER_GOTO_EXCEPTION_HANDLER() \ + do { \ + SAVE_PC(); \ + goto *dispatchTable[EcmaOpcode::LAST_OPCODE]; \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHECK_SWITCH_TO_DEBUGGER_TABLE() \ + if (Runtime::GetCurrent()->IsDebugMode()) { \ + dispatchTable = debugDispatchTable; \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define REAL_GOTO_DISPATCH_OPCODE(opcode) \ + do { \ + goto *instDispatchTable[opcode]; \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define REAL_GOTO_EXCEPTION_HANDLER() \ + do { \ + SAVE_PC(); \ + goto *instDispatchTable[EcmaOpcode::LAST_OPCODE]; \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTERPRETER_RETURN_IF_ABRUPT(result) \ + do { \ + if (result.IsException()) { \ + INTERPRETER_GOTO_EXCEPTION_HANDLER(); \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define NOTIFY_DEBUGGER_EVENT() \ + do { \ + SAVE_ACC(); \ + SAVE_PC(); \ + NotifyBytecodePcChanged(thread); \ + RESTORE_ACC(); \ + } while (false) + +#if ENABLE_IC +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UPDATE_HOTNESS_COUNTER(thread, sp, offset) (UpdateHotnessCounter(thread, sp, offset)) +#else +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UPDATE_HOTNESS_COUNTER(thread, sp, offset) static_cast(0) +#endif + +#define READ_INST_OP() READ_INST_8(0); +#define READ_INST_4_0(inst_) (READ_INST_8(1) & 0xf) +#define READ_INST_4_1(inst_) ((READ_INST_8(1) >> 4) & 0xf) +#define READ_INST_8_0(inst_) (READ_INST_8(1) & 0xff) +#define READ_INST_8_1(inst_) (READ_INST_8(2) & 0xff) +#define READ_INST_8_2(inst_) (READ_INST_8(3) & 0xff) +#define READ_INST_8_3(inst_) (READ_INST_8(4) & 0xff) +#define READ_INST_8_4(inst_) (READ_INST_8(5) & 0xff) +#define READ_INST_8_5(inst_) (READ_INST_8(6) & 0xff) +#define READ_INST_8_6(inst_) (READ_INST_8(7) & 0xff) +#define READ_INST_8(offset) (*(pc + (offset))) +#define MOVE_AND_READ_INST_8(currentInst, offset) \ + (currentInst) <<= 8; \ + (currentInst) += READ_INST_8(offset); + +#define READ_INST_16_0() READ_INST_16(2) +#define READ_INST_16_1() READ_INST_16(3) +#define READ_INST_16_2() READ_INST_16(4) +#define READ_INST_16_3() READ_INST_16(5) +#define READ_INST_16_4() READ_INST_16(6) +#define READ_INST_16_5() READ_INST_16(7) +#define READ_INST_16(offset) \ + ({ \ + uint16_t currentInst = READ_INST_8(offset); \ + MOVE_AND_READ_INST_8(currentInst, offset - 1); \ + }) + +#define READ_INST_32_0() READ_INST_32(4) +#define READ_INST_32_1() READ_INST_32(5) +#define READ_INST_32(offset) \ + ({ \ + uint32_t currentInst = READ_INST_8(offset); \ + MOVE_AND_READ_INST_8(currentInst, offset - 1); \ + MOVE_AND_READ_INST_8(currentInst, offset - 2); \ + MOVE_AND_READ_INST_8(currentInst, offset - 3); \ + }) + +#define READ_INST_64_0() \ + ({ \ + uint64_t currentInst = READ_INST_8(8); \ + MOVE_AND_READ_INST_8(currentInst, 7); \ + MOVE_AND_READ_INST_8(currentInst, 6); \ + MOVE_AND_READ_INST_8(currentInst, 5); \ + MOVE_AND_READ_INST_8(currentInst, 4); \ + MOVE_AND_READ_INST_8(currentInst, 3); \ + MOVE_AND_READ_INST_8(currentInst, 2); \ + MOVE_AND_READ_INST_8(currentInst, 1); \ + }) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GET_VREG(idx) (sp[idx]) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GET_VREG_VALUE(idx) (JSTaggedValue(sp[idx])) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_VREG(idx, val) (sp[idx] = (val)); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) +#define GET_ACC() (acc) // NOLINT(cppcoreguidelines-macro-usage) +#define SET_ACC(val) (acc = val); // NOLINT(cppcoreguidelines-macro-usage) + +JSTaggedValue EcmaInterpreter::ExecuteNative(JSThread *thread, const CallParams& params) +{ + JSTaggedType *sp = thread->GetCurrentSPFrame(); + JSMethod *methodToCall = params.callTarget->GetCallTarget(); + ASSERT(methodToCall->GetNumVregs() == 0); + uint32_t numActualArgs = params.argc + RESERVED_CALL_ARGCOUNT; + // Tags and values of thread, new_tgt and argv_length are put into frames too for native frames + // NOLINTNEXTLINE(hicpp-signed-bitwise) + size_t frameSize = FRAME_STATE_SIZE + numActualArgs; + JSTaggedType *newSp = sp - frameSize; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (thread->DoStackOverflowCheck(newSp) || thread->HasPendingException()) { + return JSTaggedValue::Undefined(); + } + + EcmaRuntimeCallInfo ecmaRuntimeCallInfo(thread, numActualArgs, reinterpret_cast(newSp)); + newSp[RESERVED_INDEX_CALL_TARGET] = reinterpret_cast(params.callTarget); + newSp[RESERVED_INDEX_NEW_TARGET] = params.newTarget; + newSp[RESERVED_INDEX_THIS] = params.thisArg; + for (size_t i = 0; i < params.argc; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[i + RESERVED_CALL_ARGCOUNT] = params.argv[i]; + } + + FrameState *state = GET_FRAME(newSp); + state->prev = sp; + state->pc = nullptr; + state->sp = newSp; + state->method = methodToCall; + thread->SetCurrentSPFrame(newSp); + LOG(DEBUG, INTERPRETER) << "Entry: Runtime Call."; + JSTaggedValue tagged = + reinterpret_cast(const_cast(methodToCall->GetNativePointer()))(&ecmaRuntimeCallInfo); + LOG(DEBUG, INTERPRETER) << "Exit: Runtime Call."; + thread->SetCurrentSPFrame(sp); + return tagged; +} + +JSTaggedValue EcmaInterpreter::Execute(JSThread *thread, const CallParams& params) +{ + JSMethod *method = params.callTarget->GetCallTarget(); + ASSERT(thread->IsEcmaInterpreter()); + if (method->IsNative()) { + return EcmaInterpreter::ExecuteNative(thread, params); + } + + JSTaggedType *originalPrevSp = thread->GetCurrentSPFrame(); + + // push break state + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + JSTaggedType *newSp = originalPrevSp - FRAME_STATE_SIZE; + if (thread->DoStackOverflowCheck(newSp) || thread->HasPendingException()) { + return JSTaggedValue::Undefined(); + } + FrameState *breakState = GET_FRAME(newSp); + breakState->pc = nullptr; + breakState->sp = nullptr; + breakState->prev = originalPrevSp; + breakState->numActualArgs = 0; + JSTaggedType *prevSp = newSp; + + uint32_t numActualArgs = params.argc + RESERVED_CALL_ARGCOUNT; + // push method frame + uint32_t numVregs = method->GetNumVregs(); + uint32_t numDeclaredArgs = method->GetNumArgs(); + size_t frameSize = FRAME_STATE_SIZE + numVregs + std::max(numDeclaredArgs, numActualArgs); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp = prevSp - frameSize; + if (thread->DoStackOverflowCheck(newSp) || thread->HasPendingException()) { + return JSTaggedValue::Undefined(); + } + // push args + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[numVregs + RESERVED_INDEX_CALL_TARGET] = reinterpret_cast(params.callTarget); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[numVregs + RESERVED_INDEX_NEW_TARGET] = params.newTarget; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[numVregs + RESERVED_INDEX_THIS] = params.thisArg; + for (size_t i = 0; i < params.argc; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[numVregs + i + RESERVED_CALL_ARGCOUNT] = params.argv[i]; + } + InterpreterFrameCopyArgs(newSp, numVregs, numActualArgs, numDeclaredArgs); + + const uint8_t *pc = JSMethod::Cast(method)->GetBytecodeArray(); + FrameState *state = GET_FRAME(newSp); + state->pc = pc; + state->sp = newSp; + state->method = method; + state->acc = JSTaggedValue::Hole(); + JSHandle callTargetHandle(thread, params.callTarget); + ASSERT(callTargetHandle->IsJSFunction()); + JSHandle thisFunc = JSHandle::Cast(callTargetHandle); + ConstantPool *constpool = ConstantPool::Cast(thisFunc->GetConstantPool().GetTaggedObject()); + state->constpool = constpool; + state->profileTypeInfo = thisFunc->GetProfileTypeInfo(); + state->prev = prevSp; + state->numActualArgs = numActualArgs; + + JSTaggedValue env = thisFunc->GetLexicalEnv(); + state->env = env; + + thread->SetCurrentSPFrame(newSp); + + LOG(DEBUG, INTERPRETER) << "break Entry: Runtime Call " << std::hex << reinterpret_cast(newSp) << " " + << std::hex << reinterpret_cast(pc); + + EcmaInterpreter::RunInternal(thread, constpool, pc, newSp); + + // NOLINTNEXTLINE(readability-identifier-naming) + const JSTaggedValue resAcc = state->acc; + // pop frame + thread->SetCurrentSPFrame(originalPrevSp); + + return resAcc; +} + +JSTaggedValue EcmaInterpreter::GeneratorReEnterInterpreter(JSThread *thread, JSHandle context) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle func = JSHandle::Cast(JSHandle(thread, context->GetMethod())); + JSMethod *method = func->GetCallTarget(); + + JSTaggedType *currentSp = thread->GetCurrentSPFrame(); + + // push break frame + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + JSTaggedType *breakSp = currentSp - FRAME_STATE_SIZE; + if (thread->DoStackOverflowCheck(breakSp) || thread->HasPendingException()) { + return JSTaggedValue::Exception(); + } + FrameState *breakState = GET_FRAME(breakSp); + breakState->pc = nullptr; + breakState->sp = nullptr; + breakState->prev = currentSp; + breakState->numActualArgs = 0; + + // create new frame and resume sp and pc + uint32_t nregs = context->GetNRegs().GetInt(); + size_t newFrameSize = FRAME_STATE_SIZE + nregs; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic + JSTaggedType *newSp = breakSp - newFrameSize; + if (thread->DoStackOverflowCheck(newSp) || thread->HasPendingException()) { + return JSTaggedValue::Exception(); + } + JSHandle regsArray(thread, context->GetRegsArray()); + for (size_t i = 0; i < nregs; i++) { + newSp[i] = regsArray->Get(i).GetRawData(); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + ConstantPool *constpool = ConstantPool::Cast(func->GetConstantPool().GetTaggedObject()); + JSTaggedValue pcOffset = context->GetBCOffset(); + // pc = first_inst + offset + size(Opcode::SUSPENDGENERATOR_IMM8_V8_V8) + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const uint8_t *resumePc = JSMethod::Cast(method)->GetBytecodeArray() + static_cast(pcOffset.GetInt()) + + BytecodeInstruction::Size(BytecodeInstruction::Format::IMM8_V8_V8); + + FrameState *state = GET_FRAME(newSp); + state->pc = resumePc; + state->sp = newSp; + state->method = method; + state->constpool = constpool; + state->profileTypeInfo = func->GetProfileTypeInfo(); + state->acc = context->GetAcc(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + state->prev = breakSp; + JSTaggedValue env = context->GetLexicalEnv(); + state->env = env; + // execute interpreter + thread->SetCurrentSPFrame(newSp); + EcmaInterpreter::RunInternal(thread, constpool, resumePc, newSp); + + JSTaggedValue res = state->acc; + // pop frame + thread->SetCurrentSPFrame(currentSp); + + return res; +} + +void EcmaInterpreter::ChangeGenContext(JSThread *thread, JSHandle context) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle func = JSHandle::Cast(JSHandle(thread, context->GetMethod())); + JSMethod *method = func->GetCallTarget(); + + JSTaggedType *currentSp = thread->GetCurrentSPFrame(); + + // push break frame + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + JSTaggedType *breakSp = currentSp - FRAME_STATE_SIZE; + if (thread->DoStackOverflowCheck(breakSp) || thread->HasPendingException()) { + return; + } + FrameState *breakState = GET_FRAME(breakSp); + breakState->pc = nullptr; + breakState->sp = nullptr; + breakState->prev = currentSp; + + // create new frame and resume sp and pc + uint32_t nregs = context->GetNRegs().GetInt(); + size_t newFrameSize = FRAME_STATE_SIZE + nregs; + + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic + JSTaggedType *newSp = breakSp - newFrameSize; + if (thread->DoStackOverflowCheck(newSp) || thread->HasPendingException()) { + return; + } + JSHandle regsArray(thread, context->GetRegsArray()); + for (size_t i = 0; i < nregs; i++) { + newSp[i] = regsArray->Get(i).GetRawData(); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + ConstantPool *constpool = ConstantPool::Cast(func->GetConstantPool().GetTaggedObject()); + JSTaggedValue pcOffset = context->GetBCOffset(); + // pc = first_inst + offset + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const uint8_t *pc = JSMethod::Cast(method)->GetBytecodeArray() + static_cast(pcOffset.GetInt()); + + FrameState *state = GET_FRAME(newSp); + state->pc = pc; + state->sp = newSp; + state->method = method; + state->constpool = constpool; + state->profileTypeInfo = func->GetProfileTypeInfo(); + state->acc = context->GetAcc(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + state->prev = breakSp; + state->env = context->GetLexicalEnv(); + + thread->SetCurrentSPFrame(newSp); +} + +void EcmaInterpreter::ResumeContext(JSThread *thread) +{ + JSTaggedType *sp = thread->GetCurrentSPFrame(); + FrameState *state = GET_FRAME(sp); + thread->SetCurrentSPFrame(state->prev); +} + +void EcmaInterpreter::NotifyBytecodePcChanged(JSThread *thread) +{ + EcmaFrameHandler frameHandler(thread); + for (; frameHandler.HasFrame(); frameHandler.PrevFrame()) { + JSMethod *method = frameHandler.GetMethod(); + // Skip builtins method + if (method->IsNative()) { + continue; + } + auto bcOffset = frameHandler.GetBytecodeOffset(); + Runtime::GetCurrent()->GetNotificationManager()->BytecodePcChangedEvent(thread, method, bcOffset); + return; + } +} + +// NOLINTNEXTLINE(readability-function-size) +NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, ConstantPool *constpool, const uint8_t *pc, + JSTaggedType *sp) +{ + INTERPRETER_TRACE(thread, RunInternal); + uint8_t opcode = READ_INST_OP(); + JSTaggedValue acc = JSTaggedValue::Hole(); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVm->GetGlobalEnv(); + JSTaggedValue globalObj = globalEnv->GetGlobalObject(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + constexpr size_t numOps = 0x100; + + static std::array instDispatchTable{ +#include "templates/instruction_dispatch.inl" + }; + + static std::array debugDispatchTable{ +#include "templates/debugger_instruction_dispatch.inl" + }; + + std::array dispatchTable = instDispatchTable; + CHECK_SWITCH_TO_DEBUGGER_TABLE(); + goto *dispatchTable[opcode]; + + HANDLE_OPCODE(HANDLE_MOV_V4_V4) { + uint16_t vdst = READ_INST_4_0(); + uint16_t vsrc = READ_INST_4_1(); + LOG_INST() << "mov v" << vdst << ", v" << vsrc; + uint64_t value = GET_VREG(vsrc); + SET_VREG(vdst, value) + DISPATCH(BytecodeInstruction::Format::V4_V4); + } + + HANDLE_OPCODE(HANDLE_MOV_DYN_V8_V8) { + uint16_t vdst = READ_INST_8_0(); + uint16_t vsrc = READ_INST_8_1(); + LOG_INST() << "mov.dyn v" << vdst << ", v" << vsrc; + uint64_t value = GET_VREG(vsrc); + SET_VREG(vdst, value) + DISPATCH(BytecodeInstruction::Format::V8_V8); + } + HANDLE_OPCODE(HANDLE_MOV_DYN_V16_V16) { + uint16_t vdst = READ_INST_16_0(); + uint16_t vsrc = READ_INST_16_2(); + LOG_INST() << "mov.dyn v" << vdst << ", v" << vsrc; + uint64_t value = GET_VREG(vsrc); + SET_VREG(vdst, value) + DISPATCH(BytecodeInstruction::Format::V16_V16); + } + HANDLE_OPCODE(HANDLE_LDA_STR_ID32) { + uint32_t stringId = READ_INST_32_0(); + LOG_INST() << "lda.str " << std::hex << stringId; + SET_ACC(constpool->GetObjectFromCache(stringId)); + DISPATCH(BytecodeInstruction::Format::ID32); + } + HANDLE_OPCODE(HANDLE_JMP_IMM8) { + int8_t offset = READ_INST_8_0(); + UPDATE_HOTNESS_COUNTER(thread, sp, offset); + LOG_INST() << "jmp " << std::hex << static_cast(offset); + DISPATCH_OFFSET(offset); + } + HANDLE_OPCODE(HANDLE_JMP_IMM16) { + int16_t offset = READ_INST_16_0(); + UPDATE_HOTNESS_COUNTER(thread, sp, offset); + LOG_INST() << "jmp " << std::hex << static_cast(offset); + DISPATCH_OFFSET(offset); + } + HANDLE_OPCODE(HANDLE_JMP_IMM32) { + int32_t offset = READ_INST_32_0(); + UPDATE_HOTNESS_COUNTER(thread, sp, offset); + LOG_INST() << "jmp " << std::hex << offset; + DISPATCH_OFFSET(offset); + } + HANDLE_OPCODE(HANDLE_JEQZ_IMM8) { + int8_t offset = READ_INST_8_0(); + UPDATE_HOTNESS_COUNTER(thread, sp, offset); + LOG_INST() << "jeqz ->\t" + << "cond jmpz " << std::hex << static_cast(offset); + if (GET_ACC() == JSTaggedValue::False() || (GET_ACC().IsInt() && GET_ACC().GetInt() == 0) || + (GET_ACC().IsDouble() && GET_ACC().GetDouble() == 0)) { + DISPATCH_OFFSET(offset); + } else { + DISPATCH(BytecodeInstruction::Format::IMM8); + } + } + HANDLE_OPCODE(HANDLE_JEQZ_IMM16) { + int16_t offset = READ_INST_16_0(); + UPDATE_HOTNESS_COUNTER(thread, sp, offset); + LOG_INST() << "jeqz ->\t" + << "cond jmpz " << std::hex << static_cast(offset); + if (GET_ACC() == JSTaggedValue::False() || (GET_ACC().IsInt() && GET_ACC().GetInt() == 0) || + (GET_ACC().IsDouble() && GET_ACC().GetDouble() == 0)) { + DISPATCH_OFFSET(offset); + } else { + DISPATCH(BytecodeInstruction::Format::IMM16); + } + } + HANDLE_OPCODE(HANDLE_JNEZ_IMM8) { + int8_t offset = READ_INST_8_0(); + LOG_INST() << "jnez ->\t" + << "cond jmpz " << std::hex << static_cast(offset); + if (GET_ACC() == JSTaggedValue::True() || (GET_ACC().IsInt() && GET_ACC().GetInt() != 0) || + (GET_ACC().IsDouble() && GET_ACC().GetDouble() != 0)) { + DISPATCH_OFFSET(offset); + } else { + DISPATCH(BytecodeInstruction::Format::IMM8); + } + } + HANDLE_OPCODE(HANDLE_JNEZ_IMM16) { + int16_t offset = READ_INST_16_0(); + LOG_INST() << "jnez ->\t" + << "cond jmpz " << std::hex << static_cast(offset); + if (GET_ACC() == JSTaggedValue::True() || (GET_ACC().IsInt() && GET_ACC().GetInt() != 0) || + (GET_ACC().IsDouble() && GET_ACC().GetDouble() != 0)) { + DISPATCH_OFFSET(offset); + } else { + DISPATCH(BytecodeInstruction::Format::IMM16); + } + } + HANDLE_OPCODE(HANDLE_LDA_DYN_V8) { + uint16_t vsrc = READ_INST_8_0(); + LOG_INST() << "lda.dyn v" << vsrc; + uint64_t value = GET_VREG(vsrc); + SET_ACC(JSTaggedValue(value)) + DISPATCH(BytecodeInstruction::Format::V8); + } + HANDLE_OPCODE(HANDLE_STA_DYN_V8) { + uint16_t vdst = READ_INST_8_0(); + LOG_INST() << "sta.dyn v" << vdst; + SET_VREG(vdst, GET_ACC().GetRawData()) + DISPATCH(BytecodeInstruction::Format::V8); + } + HANDLE_OPCODE(HANDLE_LDAI_DYN_IMM32) { + int32_t imm = READ_INST_32_0(); + LOG_INST() << "ldai.dyn " << std::hex << imm; + SET_ACC(JSTaggedValue(imm)) + DISPATCH(BytecodeInstruction::Format::IMM32); + } + + HANDLE_OPCODE(HANDLE_FLDAI_DYN_IMM64) { + auto imm = bit_cast(READ_INST_64_0()); + LOG_INST() << "fldai.dyn " << imm; + SET_ACC(JSTaggedValue(imm)) + DISPATCH(BytecodeInstruction::Format::IMM64); + } + { + uint32_t actualNumArgs; + uint32_t funcReg; + bool callThis; + bool callRange; + + HANDLE_OPCODE(HANDLE_BUILTIN_CALLARG0DYN_IMM8_V8) { + funcReg = READ_INST_8_1(); + actualNumArgs = ActualNumArgsOfCall::CALLARG0; + + LOG_INST() << "callarg0.dyn " + << "v" << funcReg; + callRange = false; + callThis = false; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto, hicpp-avoid-goto) + goto handlerCall; + } + HANDLE_OPCODE(HANDLE_BUILTIN_CALLARG1DYN_IMM8_V8_V8) { + funcReg = READ_INST_8_1(); + actualNumArgs = ActualNumArgsOfCall::CALLARG1; + + LOG_INST() << "callarg1.dyn " + << "v" << funcReg << ", v" << READ_INST_8_2(); + callRange = false; + callThis = false; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto, hicpp-avoid-goto) + goto handlerCall; + } + HANDLE_OPCODE(HANDLE_BUILTIN_CALLARGS2DYN_IMM8_V8_V8_V8) { + funcReg = READ_INST_8_1(); + actualNumArgs = ActualNumArgsOfCall::CALLARGS2; + + LOG_INST() << "callargs2.dyn " + << "v" << funcReg << ", v" << READ_INST_8_2() << ", v" << READ_INST_8_3(); + callRange = false; + callThis = false; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto, hicpp-avoid-goto) + goto handlerCall; + } + HANDLE_OPCODE(HANDLE_BUILTIN_CALLARGS3DYN_IMM8_V8_V8_V8_V8) { + funcReg = READ_INST_8_1(); + actualNumArgs = ActualNumArgsOfCall::CALLARGS3; + + LOG_INST() << "callargs3.dyn " + << "v" << funcReg << ", v" << READ_INST_8_2() << ", v" << READ_INST_8_3() + << ", v" << READ_INST_8_4(); + callRange = false; + callThis = false; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto, hicpp-avoid-goto) + goto handlerCall; + } + HANDLE_OPCODE(HANDLE_BUILTIN_CALLITHISRANGEDYN_IMM8_IMM16_V8) { + actualNumArgs = READ_INST_16_1() + 2; // 2: func and newTarget + funcReg = READ_INST_8_3(); + + LOG_INST() << "calli.dyn.this.range " << actualNumArgs << ", v" << funcReg; + callRange = true; + callThis = true; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto, hicpp-avoid-goto) + goto handlerCall; + } + HANDLE_OPCODE(HANDLE_BUILTIN_CALLSPREADDYN_IMM8_V8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + uint16_t v1 = READ_INST_8_2(); + uint16_t v2 = READ_INST_8_3(); + LOG_INST() << "intrinsics::callspreaddyn" + << " v" << v0 << " v" << v1 << " v" << v2; + JSTaggedValue func = GET_VREG_VALUE(v0); + JSTaggedValue obj = GET_VREG_VALUE(v1); + JSTaggedValue array = GET_VREG_VALUE(v2); + + JSTaggedValue res = SlowRuntimeStub::CallSpreadDyn(thread, func, obj, array); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_CALLIRANGEDYN_IMM8_IMM16_V8) { + actualNumArgs = READ_INST_16_1() + NUM_MANDATORY_JSFUNC_ARGS; + funcReg = READ_INST_8_3(); + callRange = true; + callThis = false; + LOG_INST() << "calli.rangedyn " << actualNumArgs << ", v" << funcReg; + + handlerCall: + JSTaggedValue func = GET_VREG_VALUE(funcReg); + if (!func.IsCallable()) { + { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle error = factory->GetJSError(ErrorType::TYPE_ERROR, "is not callable"); + thread->SetException(error.GetTaggedValue()); + } + INTERPRETER_GOTO_EXCEPTION_HANDLER(); + } + ECMAObject *thisFunc = ECMAObject::Cast(func.GetTaggedObject()); + JSMethod *methodToCall = thisFunc->GetCallTarget(); + if (methodToCall->IsNative()) { + ASSERT(methodToCall->GetNumVregs() == 0); + // Tags and values of thread, new_tgt and argv_length are put into frames too for native frames + // NOLINTNEXTLINE(hicpp-signed-bitwise) + size_t frameSize = FRAME_STATE_SIZE + actualNumArgs; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + JSTaggedType *newSp = sp - frameSize; + if (thread->DoStackOverflowCheck(newSp) || thread->HasPendingException()) { + INTERPRETER_GOTO_EXCEPTION_HANDLER(); + } + EcmaRuntimeCallInfo ecmaRuntimeCallInfo(thread, actualNumArgs, + reinterpret_cast(newSp)); + uint32_t startIdx = 0; + // func + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[startIdx++] = static_cast(ToUintPtr(thisFunc)); + // new target + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[startIdx++] = JSTaggedValue::VALUE_UNDEFINED; + + if (callRange) { + size_t copyArgs = actualNumArgs - 2; // 2: skip func and new target + if (!callThis) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[startIdx++] = JSTaggedValue::VALUE_UNDEFINED; + // skip this + copyArgs--; + } + for (size_t i = 1; i <= copyArgs; i++) { + JSTaggedValue arg = GET_VREG_VALUE(funcReg + i); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[startIdx++] = arg.GetRawData(); + } + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[startIdx] = JSTaggedValue::VALUE_UNDEFINED; + switch (actualNumArgs) { + case ActualNumArgsOfCall::CALLARGS3: { + uint32_t reg = READ_INST_8_4(); + JSTaggedValue arg = GET_VREG_VALUE(reg); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[ActualNumArgsOfCall::CALLARGS3 - 1] = arg.GetRawData(); + [[fallthrough]]; + } + case ActualNumArgsOfCall::CALLARGS2: { + uint32_t reg = READ_INST_8_3(); + JSTaggedValue arg = GET_VREG_VALUE(reg); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[ActualNumArgsOfCall::CALLARGS2 - 1] = arg.GetRawData(); + [[fallthrough]]; + } + case ActualNumArgsOfCall::CALLARG1: { + uint32_t reg = READ_INST_8_2(); + JSTaggedValue arg = GET_VREG_VALUE(reg); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[ActualNumArgsOfCall::CALLARG1 - 1] = arg.GetRawData(); + [[fallthrough]]; + } + case ActualNumArgsOfCall::CALLARG0: { + break; + } + default: + UNREACHABLE(); + } + } + + FrameState *state = GET_FRAME(newSp); + state->prev = sp; + state->pc = nullptr; + state->sp = newSp; + state->method = methodToCall; + thread->SetCurrentSPFrame(newSp); + LOG(DEBUG, INTERPRETER) << "Entry: Runtime Call."; + JSTaggedValue retValue = reinterpret_cast( + const_cast(methodToCall->GetNativePointer()))(&ecmaRuntimeCallInfo); + if (UNLIKELY(thread->HasPendingException())) { + INTERPRETER_GOTO_EXCEPTION_HANDLER(); + } + LOG(DEBUG, INTERPRETER) << "Exit: Runtime Call."; + thread->SetCurrentSPFrame(sp); + SET_ACC(retValue); + size_t jumpSize = GetJumpSizeAfterCall(pc); + DISPATCH_OFFSET(jumpSize); + } else { + if (JSFunction::Cast(thisFunc)->IsClassConstructor()) { + { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle error = + factory->GetJSError(ErrorType::TYPE_ERROR, "class constructor cannot called without 'new'"); + thread->SetException(error.GetTaggedValue()); + } + INTERPRETER_GOTO_EXCEPTION_HANDLER(); + } + SAVE_PC(); + uint32_t numVregs = methodToCall->GetNumVregs(); + uint32_t numDeclaredArgs = methodToCall->GetNumArgs(); + size_t frameSize = FRAME_STATE_SIZE + numVregs + std::max(numDeclaredArgs, actualNumArgs); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + JSTaggedType *newSp = sp - frameSize; + if (thread->DoStackOverflowCheck(newSp) || thread->HasPendingException()) { + INTERPRETER_GOTO_EXCEPTION_HANDLER(); + } + + // copy args + uint32_t startIdx = numVregs; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[startIdx++] = static_cast(ToUintPtr(thisFunc)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[startIdx++] = JSTaggedValue::VALUE_UNDEFINED; + if (callRange) { + size_t copyArgs = actualNumArgs - 2; // 2: skip func and new target + if (!callThis) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[startIdx++] = JSTaggedValue::VALUE_UNDEFINED; + // skip this + copyArgs--; + } + for (size_t i = 1; i <= copyArgs; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[startIdx++] = sp[funcReg + i]; + } + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[startIdx] = JSTaggedValue::VALUE_UNDEFINED; + switch (actualNumArgs) { + case ActualNumArgsOfCall::CALLARGS3: + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[numVregs + ActualNumArgsOfCall::CALLARGS3 - 1] = sp[READ_INST_8_4()]; + [[fallthrough]]; + case ActualNumArgsOfCall::CALLARGS2: + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[numVregs + ActualNumArgsOfCall::CALLARGS2 - 1] = sp[READ_INST_8_3()]; + [[fallthrough]]; + case ActualNumArgsOfCall::CALLARG1: + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[numVregs + ActualNumArgsOfCall::CALLARG1 - 1] = sp[READ_INST_8_2()]; + [[fallthrough]]; + case ActualNumArgsOfCall::CALLARG0: + break; + default: + UNREACHABLE(); + } + } + InterpreterFrameCopyArgs(newSp, numVregs, actualNumArgs, numDeclaredArgs); + + FrameState *state = GET_FRAME(newSp); + state->prev = sp; + state->pc = pc = JSMethod::Cast(methodToCall)->GetBytecodeArray(); + state->sp = sp = newSp; + state->method = methodToCall; + state->acc = JSTaggedValue::Hole(); + state->constpool = constpool = + ConstantPool::Cast(JSFunction::Cast(thisFunc)->GetConstantPool().GetTaggedObject()); + state->profileTypeInfo = JSFunction::Cast(thisFunc)->GetProfileTypeInfo(); + state->numActualArgs = actualNumArgs; + JSTaggedValue env = JSFunction::Cast(thisFunc)->GetLexicalEnv(); + state->env = env; + + thread->SetCurrentSPFrame(newSp); + LOG(DEBUG, INTERPRETER) << "Entry: Runtime Call " << std::hex << reinterpret_cast(sp) << " " + << std::hex << reinterpret_cast(pc); + DISPATCH_OFFSET(0); + } + } + } + HANDLE_OPCODE(HANDLE_RETURN_DYN) { + LOG_INST() << "return.dyn"; + FrameState *state = GET_FRAME(sp); + LOG(DEBUG, INTERPRETER) << "Exit: Runtime Call " << std::hex << reinterpret_cast(state->sp) << " " + << std::hex << reinterpret_cast(state->pc); + [[maybe_unused]] auto fistPC = state->method->GetInstructions(); + UPDATE_HOTNESS_COUNTER(thread, sp, -(pc - fistPC)); + sp = state->prev; + ASSERT(sp != nullptr); + FrameState *prevState = GET_FRAME(sp); + pc = prevState->pc; + + // break frame + if (pc == nullptr) { + state->acc = acc; + return; + } + thread->SetCurrentSPFrame(sp); + + constpool = prevState->constpool; + + size_t jumpSize = GetJumpSizeAfterCall(pc); + DISPATCH_OFFSET(jumpSize); + } + HANDLE_OPCODE(HANDLE_BUILTIN_RETURNUNDEFINED_IMM8) { + LOG_INST() << "return.undefined"; + FrameState *state = GET_FRAME(sp); + LOG(DEBUG, INTERPRETER) << "Exit: Runtime Call " << std::hex << reinterpret_cast(sp) << " " + << std::hex << reinterpret_cast(state->pc); + [[maybe_unused]] auto fistPC = state->method->GetInstructions(); + UPDATE_HOTNESS_COUNTER(thread, sp, -(pc - fistPC)); + sp = state->prev; + ASSERT(sp != nullptr); + FrameState *prevState = GET_FRAME(sp); + pc = prevState->pc; + + // break frame + if (pc == nullptr) { + state->acc = JSTaggedValue::Undefined(); + return; + } + thread->SetCurrentSPFrame(sp); + + constpool = prevState->constpool; + + acc = JSTaggedValue::Undefined(); + size_t jumpSize = GetJumpSizeAfterCall(pc); + DISPATCH_OFFSET(jumpSize); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDNAN_IMM8) { + LOG_INST() << "intrinsics::ldnan"; + SET_ACC(JSTaggedValue(base::NAN_VALUE)); + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDINFINITY_IMM8) { + LOG_INST() << "intrinsics::ldinfinity"; + SET_ACC(JSTaggedValue(base::POSITIVE_INFINITY)); + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDGLOBALTHIS_IMM8) { + LOG_INST() << "intrinsics::ldglobalthis"; + SET_ACC(globalObj) + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDUNDEFINED_IMM8) { + LOG_INST() << "intrinsics::ldundefined"; + SET_ACC(JSTaggedValue::Undefined()) + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDNULL_IMM8) { + LOG_INST() << "intrinsics::ldnull"; + SET_ACC(JSTaggedValue::Null()) + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDSYMBOL_IMM8) { + LOG_INST() << "intrinsics::ldsymbol"; + SET_ACC(globalEnv->GetSymbolFunction().GetTaggedValue()); + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDGLOBAL_IMM8) { + LOG_INST() << "intrinsics::ldglobal"; + SET_ACC(globalObj) + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDTRUE_IMM8) { + LOG_INST() << "intrinsics::ldtrue"; + SET_ACC(JSTaggedValue::True()) + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDFALSE_IMM8) { + LOG_INST() << "intrinsics::ldfalse"; + SET_ACC(JSTaggedValue::False()) + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDLEXENVDYN_IMM8) { + LOG_INST() << "intrinsics::ldlexenvDyn "; + FrameState *state = GET_FRAME(sp); + JSTaggedValue currentLexenv = state->env; + SET_ACC(currentLexenv); + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_GETUNMAPPEDARGS_IMM8) { + LOG_INST() << "intrinsics::getunmappedargs"; + + FrameState *state = GET_FRAME(sp); + uint32_t numVregs = state->method->GetNumVregs(); + // Exclude func, newTarget and "this" + uint32_t actualNumArgs = state->numActualArgs - NUM_MANDATORY_JSFUNC_ARGS; + uint32_t startIdx = numVregs + NUM_MANDATORY_JSFUNC_ARGS; + + JSTaggedValue res = SlowRuntimeStub::GetUnmapedArgs(thread, sp, actualNumArgs, startIdx); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_ASYNCFUNCTIONENTER_IMM8) { + LOG_INST() << "intrinsics::asyncfunctionenter"; + JSTaggedValue res = SlowRuntimeStub::AsyncFunctionEnter(thread); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDBOOLEAN_IMM8_V8) { + // it38 Unimplement + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDNUMBER_IMM8_V8) { + // it38 Unimplement + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDSTRING_IMM8_V8) { + // it38 Unimplement + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDBIGINT_IMM8_V8) { + // it38 Unimplement + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_TONUMBER_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::tonumber" + << " v" << v0; + JSTaggedValue value = GET_VREG_VALUE(v0); + if (value.IsNumber()) { + // fast path + SET_ACC(value); + } else { + // slow path + JSTaggedValue res = SlowRuntimeStub::ToNumber(thread, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_NEGDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::negdyn" + << " v" << v0; + JSTaggedValue value = GET_VREG_VALUE(v0); + // fast path + if (value.IsInt()) { + if (value.GetInt() == 0) { + SET_ACC(JSTaggedValue(-0.0)); + } else { + SET_ACC(JSTaggedValue(-value.GetInt())); + } + } else if (value.IsDouble()) { + SET_ACC(JSTaggedValue(-value.GetDouble())); + } else { // slow path + JSTaggedValue res = SlowRuntimeStub::NegDyn(thread, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_NOTDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::notdyn" + << " v" << v0; + JSTaggedValue value = GET_VREG_VALUE(v0); + int32_t number; + // number, fast path + if (value.IsInt()) { + number = static_cast(value.GetInt()); + SET_ACC(JSTaggedValue(~number)); // NOLINT(hicpp-signed-bitwise) + } else if (value.IsDouble()) { + number = base::NumberHelper::DoubleToInt(value.GetDouble(), base::INT32_BITS); + SET_ACC(JSTaggedValue(~number)); // NOLINT(hicpp-signed-bitwise) + } else { + // slow path + JSTaggedValue res = SlowRuntimeStub::NotDyn(thread, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_INCDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::incdyn" + << " v" << v0; + + JSTaggedValue value = GET_VREG_VALUE(v0); + // number fast path + if (value.IsInt()) { + int32_t a0 = value.GetInt(); + if (UNLIKELY(a0 == INT32_MAX)) { + auto ret = static_cast(a0) + 1.0; + SET_ACC(JSTaggedValue(ret)) + } else { + SET_ACC(JSTaggedValue(a0 + 1)) + } + } else if (value.IsDouble()) { + SET_ACC(JSTaggedValue(value.GetDouble() + 1.0)) + } else { + // slow path + JSTaggedValue res = SlowRuntimeStub::IncDyn(thread, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DECDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::decdyn" + << " v" << v0; + + JSTaggedValue value = GET_VREG_VALUE(v0); + // number, fast path + if (value.IsInt()) { + int32_t a0 = value.GetInt(); + if (UNLIKELY(a0 == INT32_MIN)) { + auto ret = static_cast(a0) - 1.0; + SET_ACC(JSTaggedValue(ret)) + } else { + SET_ACC(JSTaggedValue(a0 - 1)) + } + } else if (value.IsDouble()) { + SET_ACC(JSTaggedValue(value.GetDouble() - 1.0)) + } else { + // slow path + JSTaggedValue res = SlowRuntimeStub::DecDyn(thread, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_THROWDYN_IMM8) { + LOG_INST() << "intrinsics::throwdyn"; + SlowRuntimeStub::ThrowDyn(thread, GET_ACC()); + INTERPRETER_GOTO_EXCEPTION_HANDLER(); + } + HANDLE_OPCODE(HANDLE_BUILTIN_TYPEOFDYN_IMM8) { + LOG_INST() << "intrinsics::typeofdyn"; + JSTaggedValue res = FastRuntimeStub::FastTypeOf(thread, GET_ACC()); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_TOBOOLEAN_IMM8) { + LOG_INST() << "intrinsics::toboolean"; + JSTaggedValue left = acc; + SET_ACC(left.ToBoolean() ? JSTaggedValue::True() : JSTaggedValue::False()) + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_GETPROPITERATOR_IMM8) { + LOG_INST() << "intrinsics::getpropiterator"; + JSTaggedValue res = SlowRuntimeStub::GetPropIterator(thread, GET_ACC()); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_RESUMEGENERATOR_IMM8_V8) { + LOG_INST() << "intrinsics::resumegenerator"; + uint16_t vs = READ_INST_8_1(); + JSGeneratorObject *obj = JSGeneratorObject::Cast(GET_VREG_VALUE(vs).GetTaggedObject()); + SET_ACC(obj->GetResumeResult()); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_GETRESUMEMODE_IMM8_V8) { + LOG_INST() << "intrinsics::getresumemode"; + uint16_t vs = READ_INST_8_1(); + JSGeneratorObject *obj = JSGeneratorObject::Cast(GET_VREG_VALUE(vs).GetTaggedObject()); + SET_ACC(obj->GetResumeMode()); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_GETITERATOR_IMM8) { + LOG_INST() << "intrinsics::getiterator"; + JSTaggedValue obj = GET_ACC(); + + // fast path: Generator obj is already store in acc + if (!obj.IsGeneratorObject()) { + // slow path + JSTaggedValue res = SlowRuntimeStub::GetIterator(thread, obj); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_THROWUNDEFINED_IMM8_V8) { + // the instrunction has beed retired + LOG_INST() << "-------------"; + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_THROWCONSTASSIGNMENT_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "throwconstassignment" + << " v" << v0; + SlowRuntimeStub::ThrowConstAssignment(thread, GET_VREG_VALUE(v0)); + INTERPRETER_GOTO_EXCEPTION_HANDLER(); + } + HANDLE_OPCODE(HANDLE_BUILTIN_THROWTHROWNOTEXISTS_IMM8) { + LOG_INST() << "throwthrownotexists"; + + SlowRuntimeStub::ThrowThrowNotExists(thread); + INTERPRETER_GOTO_EXCEPTION_HANDLER(); + } + HANDLE_OPCODE(HANDLE_BUILTIN_THROWPATTERNNONCOERCIBLE_IMM8) { + LOG_INST() << "throwpatternnoncoercible"; + + SlowRuntimeStub::ThrowPatternNonCoercible(thread); + INTERPRETER_GOTO_EXCEPTION_HANDLER(); + } + HANDLE_OPCODE(HANDLE_BUILTIN_THROWIFNOTOBJECT_IMM8_V8) { + LOG_INST() << "throwifnotobject"; + uint16_t v0 = READ_INST_8_1(); + + JSTaggedValue value = GET_VREG_VALUE(v0); + // fast path + if (value.IsECMAObject()) { + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + + // slow path + SlowRuntimeStub::ThrowIfNotObject(thread); + INTERPRETER_GOTO_EXCEPTION_HANDLER(); + } + HANDLE_OPCODE(HANDLE_BUILTIN_ITERNEXT_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::iternext" + << " v" << v0; + JSTaggedValue iter = GET_VREG_VALUE(v0); + JSTaggedValue res = SlowRuntimeStub::IterNext(thread, iter); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_CLOSEITERATOR_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::closeiterator" + << " v" << v0; + JSTaggedValue iter = GET_VREG_VALUE(v0); + JSTaggedValue res = SlowRuntimeStub::CloseIterator(thread, iter); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDOBJECT_IMM8_V8_V8) { + // it38 Unimplement + LOG_INST() << "-------------"; + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDFUNCTION_IMM8_V8_V8) { + // it38 Unimplement + LOG_INST() << "-------------"; + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_ADD2DYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::add2dyn" + << " v" << v0; + int32_t a0; + int32_t a1; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = GET_ACC(); + // number, fast path + if (left.IsInt() && right.IsInt()) { + a0 = left.GetInt(); + a1 = right.GetInt(); + if ((a0 > 0 && a1 > INT32_MAX - a0) || (a0 < 0 && a1 < INT32_MIN - a0)) { + auto ret = static_cast(a0) + static_cast(a1); + SET_ACC(JSTaggedValue(ret)) + } else { + SET_ACC(JSTaggedValue(a0 + a1)) + } + } else if (left.IsNumber() && right.IsNumber()) { + double a0Double = left.IsInt() ? left.GetInt() : left.GetDouble(); + double a1Double = right.IsInt() ? right.GetInt() : right.GetDouble(); + double ret = a0Double + a1Double; + SET_ACC(JSTaggedValue(ret)) + } else { + // one or both are not number, slow path + JSTaggedValue res = SlowRuntimeStub::Add2Dyn(thread, ecmaVm, left, right); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_SUB2DYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::sub2dyn" + << " v" << v0; + int32_t a0; + int32_t a1; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = GET_ACC(); + if (left.IsInt() && right.IsInt()) { + a0 = left.GetInt(); + a1 = -right.GetInt(); + if ((a0 > 0 && a1 > INT32_MAX - a0) || (a0 < 0 && a1 < INT32_MIN - a0)) { + auto ret = static_cast(a0) + static_cast(a1); + SET_ACC(JSTaggedValue(ret)) + } else { + SET_ACC(JSTaggedValue(a0 + a1)) + } + } else if (left.IsNumber() && right.IsNumber()) { + double a0Double = left.IsInt() ? left.GetInt() : left.GetDouble(); + double a1Double = right.IsInt() ? right.GetInt() : right.GetDouble(); + double ret = a0Double - a1Double; + SET_ACC(JSTaggedValue(ret)) + } else { + // one or both are not number, slow path + JSTaggedValue res = SlowRuntimeStub::Sub2Dyn(thread, left, right); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_MUL2DYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::mul2dyn" + << " v" << v0; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = acc; + JSTaggedValue value = FastRuntimeStub::FastMul(left, right); + if (!value.IsHole()) { + SET_ACC(value); + } else { + // slow path + JSTaggedValue res = SlowRuntimeStub::Mul2Dyn(thread, left, right); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DIV2DYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::div2dyn" + << " v" << v0; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = acc; + // fast path + JSTaggedValue res = FastRuntimeStub::FastDiv(left, right); + if (!res.IsHole()) { + SET_ACC(res); + } else { + // slow path + JSTaggedValue slowRes = SlowRuntimeStub::Div2Dyn(thread, left, right); + INTERPRETER_RETURN_IF_ABRUPT(slowRes); + SET_ACC(slowRes); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_MOD2DYN_IMM8_V8) { + uint16_t vs = READ_INST_8_1(); + LOG_INST() << "intrinsics::mod2dyn" + << " v" << vs; + JSTaggedValue left = GET_VREG_VALUE(vs); + JSTaggedValue right = GET_ACC(); + // fast path + JSTaggedValue res = FastRuntimeStub::FastMod(left, right); + if (!res.IsHole()) { + SET_ACC(res); + } else { + // slow path + JSTaggedValue slowRes = SlowRuntimeStub::Mod2Dyn(thread, left, right); + INTERPRETER_RETURN_IF_ABRUPT(slowRes); + SET_ACC(slowRes); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_EQDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::eqdyn" + << " v" << v0; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = acc; + JSTaggedValue res = FastRuntimeStub::FastEqual(left, right); + if (!res.IsHole()) { + SET_ACC(res); + } else { + // slow path + res = SlowRuntimeStub::EqDyn(thread, left, right); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_NOTEQDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::noteqdyn" + << " v" << v0; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = acc; + + JSTaggedValue res = FastRuntimeStub::FastEqual(left, right); + if (!res.IsHole()) { + res = res.IsTrue() ? JSTaggedValue::False() : JSTaggedValue::True(); + SET_ACC(res); + } else { + // slow path + res = SlowRuntimeStub::NotEqDyn(thread, left, right); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LESSDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::lessdyn" + << " v" << v0; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = GET_ACC(); + if (left.IsNumber() && right.IsNumber()) { + // fast path + double valueA = left.IsInt() ? static_cast(left.GetInt()) : left.GetDouble(); + double valueB = right.IsInt() ? static_cast(right.GetInt()) : right.GetDouble(); + bool ret = JSTaggedValue::StrictNumberCompare(valueA, valueB) == ComparisonResult::LESS; + SET_ACC(ret ? JSTaggedValue::True() : JSTaggedValue::False()) + } else { + // slow path + JSTaggedValue res = SlowRuntimeStub::LessDyn(thread, left, right); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LESSEQDYN_IMM8_V8) { + uint16_t vs = READ_INST_8_1(); + LOG_INST() << "intrinsics::lesseqdyn " + << " v" << vs; + JSTaggedValue left = GET_VREG_VALUE(vs); + JSTaggedValue right = GET_ACC(); + if (left.IsNumber() && right.IsNumber()) { + // fast path + double valueA = left.IsInt() ? static_cast(left.GetInt()) : left.GetDouble(); + double valueB = right.IsInt() ? static_cast(right.GetInt()) : right.GetDouble(); + bool ret = JSTaggedValue::StrictNumberCompare(valueA, valueB) <= ComparisonResult::EQUAL; + SET_ACC(ret ? JSTaggedValue::True() : JSTaggedValue::False()) + } else { + // slow path + JSTaggedValue res = SlowRuntimeStub::LessEqDyn(thread, left, right); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_GREATERDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::greaterdyn" + << " v" << v0; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = acc; + if (left.IsNumber() && right.IsNumber()) { + // fast path + double valueA = left.IsInt() ? static_cast(left.GetInt()) : left.GetDouble(); + double valueB = right.IsInt() ? static_cast(right.GetInt()) : right.GetDouble(); + bool ret = JSTaggedValue::StrictNumberCompare(valueA, valueB) == ComparisonResult::GREAT; + SET_ACC(ret ? JSTaggedValue::True() : JSTaggedValue::False()) + } else { + // slow path + JSTaggedValue res = SlowRuntimeStub::GreaterDyn(thread, left, right); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_GREATEREQDYN_IMM8_V8) { + uint16_t vs = READ_INST_8_1(); + LOG_INST() << "intrinsics::greateqdyn " + << " v" << vs; + JSTaggedValue left = GET_VREG_VALUE(vs); + JSTaggedValue right = GET_ACC(); + if (left.IsNumber() && right.IsNumber()) { + // fast path + double valueA = left.IsInt() ? static_cast(left.GetInt()) : left.GetDouble(); + double valueB = right.IsInt() ? static_cast(right.GetInt()) : right.GetDouble(); + ComparisonResult comparison = JSTaggedValue::StrictNumberCompare(valueA, valueB); + bool ret = (comparison == ComparisonResult::GREAT) || (comparison == ComparisonResult::EQUAL); + SET_ACC(ret ? JSTaggedValue::True() : JSTaggedValue::False()) + } else { + // slow path + JSTaggedValue res = SlowRuntimeStub::GreaterEqDyn(thread, left, right); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_SHL2DYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::shl2dyn" + << " v" << v0; + int32_t opNumber0; + int32_t opNumber1; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = GET_ACC(); + // both number, fast path + if (left.IsInt() && right.IsInt()) { + opNumber0 = left.GetInt(); + opNumber1 = right.GetInt(); + } else if (left.IsNumber() && right.IsNumber()) { + opNumber0 = + left.IsInt() ? left.GetInt() : base::NumberHelper::DoubleToInt(left.GetDouble(), base::INT32_BITS); + opNumber1 = + right.IsInt() ? right.GetInt() : base::NumberHelper::DoubleToInt(right.GetDouble(), base::INT32_BITS); + } else { + // slow path + SAVE_ACC(); + JSTaggedValue taggedNumber0 = SlowRuntimeStub::ToJSTaggedValueWithInt32(thread, left); + INTERPRETER_RETURN_IF_ABRUPT(taggedNumber0); + RESTORE_ACC(); + right = GET_ACC(); // Maybe moved by GC + JSTaggedValue taggedNumber1 = SlowRuntimeStub::ToJSTaggedValueWithUint32(thread, right); + INTERPRETER_RETURN_IF_ABRUPT(taggedNumber1); + opNumber0 = taggedNumber0.GetInt(); + opNumber1 = taggedNumber1.GetInt(); + } + + uint32_t shift = + static_cast(opNumber1) & 0x1f; // NOLINT(hicpp-signed-bitwise, readability-magic-numbers) + using unsigned_type = std::make_unsigned_t; + auto ret = + static_cast(static_cast(opNumber0) << shift); // NOLINT(hicpp-signed-bitwise) + SET_ACC(JSTaggedValue(ret)) + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_SHR2DYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::shr2dyn" + << " v" << v0; + int32_t opNumber0; + int32_t opNumber1; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = GET_ACC(); + // both number, fast path + if (left.IsInt() && right.IsInt()) { + opNumber0 = left.GetInt(); + opNumber1 = right.GetInt(); + } else if (left.IsNumber() && right.IsNumber()) { + opNumber0 = + left.IsInt() ? left.GetInt() : base::NumberHelper::DoubleToInt(left.GetDouble(), base::INT32_BITS); + opNumber1 = + right.IsInt() ? right.GetInt() : base::NumberHelper::DoubleToInt(right.GetDouble(), base::INT32_BITS); + } else { + // slow path + SAVE_ACC(); + JSTaggedValue taggedNumber0 = SlowRuntimeStub::ToJSTaggedValueWithInt32(thread, left); + INTERPRETER_RETURN_IF_ABRUPT(taggedNumber0); + RESTORE_ACC(); + right = GET_ACC(); // Maybe moved by GC + JSTaggedValue taggedNumber1 = SlowRuntimeStub::ToJSTaggedValueWithUint32(thread, right); + INTERPRETER_RETURN_IF_ABRUPT(taggedNumber1); + opNumber0 = taggedNumber0.GetInt(); + opNumber1 = taggedNumber1.GetInt(); + } + + uint32_t shift = + static_cast(opNumber1) & 0x1f; // NOLINT(hicpp-signed-bitwise, readability-magic-numbers) + auto ret = static_cast(opNumber0 >> shift); // NOLINT(hicpp-signed-bitwise) + SET_ACC(JSTaggedValue(ret)) + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_ASHR2DYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::ashr2dyn" + << " v" << v0; + int32_t opNumber0; + int32_t opNumber1; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = GET_ACC(); + if (left.IsInt() && right.IsInt()) { + opNumber0 = left.GetInt(); + opNumber1 = right.GetInt(); + } else if (left.IsNumber() && right.IsNumber()) { + opNumber0 = + left.IsInt() ? left.GetInt() : base::NumberHelper::DoubleToInt(left.GetDouble(), base::INT32_BITS); + opNumber1 = + right.IsInt() ? right.GetInt() : base::NumberHelper::DoubleToInt(right.GetDouble(), base::INT32_BITS); + } else { + // slow path + SAVE_ACC(); + JSTaggedValue taggedNumber0 = SlowRuntimeStub::ToJSTaggedValueWithUint32(thread, left); + INTERPRETER_RETURN_IF_ABRUPT(taggedNumber0); + RESTORE_ACC(); + right = GET_ACC(); // Maybe moved by GC + JSTaggedValue taggedNumber1 = SlowRuntimeStub::ToJSTaggedValueWithUint32(thread, right); + INTERPRETER_RETURN_IF_ABRUPT(taggedNumber1); + opNumber0 = taggedNumber0.GetInt(); + opNumber1 = taggedNumber1.GetInt(); + } + + uint32_t shift = + static_cast(opNumber1) & 0x1f; // NOLINT(hicpp-signed-bitwise, readability-magic-numbers) + using unsigned_type = std::make_unsigned_t; + auto ret = + static_cast(static_cast(opNumber0) >> shift); // NOLINT(hicpp-signed-bitwise) + SET_ACC(JSTaggedValue(ret)) + + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_AND2DYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::and2dyn" + << " v" << v0; + int32_t opNumber0; + int32_t opNumber1; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = GET_ACC(); + // both number, fast path + if (left.IsInt() && right.IsInt()) { + opNumber0 = left.GetInt(); + opNumber1 = right.GetInt(); + } else if (left.IsNumber() && right.IsNumber()) { + opNumber0 = + left.IsInt() ? left.GetInt() : base::NumberHelper::DoubleToInt(left.GetDouble(), base::INT32_BITS); + opNumber1 = + right.IsInt() ? right.GetInt() : base::NumberHelper::DoubleToInt(right.GetDouble(), base::INT32_BITS); + } else { + // slow path + SAVE_ACC(); + JSTaggedValue taggedNumber0 = SlowRuntimeStub::ToJSTaggedValueWithInt32(thread, left); + INTERPRETER_RETURN_IF_ABRUPT(taggedNumber0); + RESTORE_ACC(); + right = GET_ACC(); // Maybe moved by GC + JSTaggedValue taggedNumber1 = SlowRuntimeStub::ToJSTaggedValueWithInt32(thread, right); + INTERPRETER_RETURN_IF_ABRUPT(taggedNumber1); + opNumber0 = taggedNumber0.GetInt(); + opNumber1 = taggedNumber1.GetInt(); + } + // NOLINT(hicpp-signed-bitwise) + auto ret = static_cast(opNumber0) & static_cast(opNumber1); + SET_ACC(JSTaggedValue(static_cast(ret))) + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_OR2DYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::or2dyn" + << " v" << v0; + int32_t opNumber0; + int32_t opNumber1; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = GET_ACC(); + // both number, fast path + if (left.IsInt() && right.IsInt()) { + opNumber0 = left.GetInt(); + opNumber1 = right.GetInt(); + } else if (left.IsNumber() && right.IsNumber()) { + opNumber0 = + left.IsInt() ? left.GetInt() : base::NumberHelper::DoubleToInt(left.GetDouble(), base::INT32_BITS); + opNumber1 = + right.IsInt() ? right.GetInt() : base::NumberHelper::DoubleToInt(right.GetDouble(), base::INT32_BITS); + } else { + // slow path + SAVE_ACC(); + JSTaggedValue taggedNumber0 = SlowRuntimeStub::ToJSTaggedValueWithInt32(thread, left); + INTERPRETER_RETURN_IF_ABRUPT(taggedNumber0); + RESTORE_ACC(); + right = GET_ACC(); // Maybe moved by GC + JSTaggedValue taggedNumber1 = SlowRuntimeStub::ToJSTaggedValueWithInt32(thread, right); + INTERPRETER_RETURN_IF_ABRUPT(taggedNumber1); + opNumber0 = taggedNumber0.GetInt(); + opNumber1 = taggedNumber1.GetInt(); + } + // NOLINT(hicpp-signed-bitwise) + auto ret = static_cast(opNumber0) | static_cast(opNumber1); + SET_ACC(JSTaggedValue(static_cast(ret))) + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_XOR2DYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + + LOG_INST() << "intrinsics::xor2dyn" + << " v" << v0; + int32_t opNumber0; + int32_t opNumber1; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = GET_ACC(); + // both number, fast path + if (left.IsInt() && right.IsInt()) { + opNumber0 = left.GetInt(); + opNumber1 = right.GetInt(); + } else if (left.IsNumber() && right.IsNumber()) { + opNumber0 = + left.IsInt() ? left.GetInt() : base::NumberHelper::DoubleToInt(left.GetDouble(), base::INT32_BITS); + opNumber1 = + right.IsInt() ? right.GetInt() : base::NumberHelper::DoubleToInt(right.GetDouble(), base::INT32_BITS); + } else { + // slow path + SAVE_ACC(); + JSTaggedValue taggedNumber0 = SlowRuntimeStub::ToJSTaggedValueWithInt32(thread, left); + INTERPRETER_RETURN_IF_ABRUPT(taggedNumber0); + RESTORE_ACC(); + right = GET_ACC(); // Maybe moved by GC + JSTaggedValue taggedNumber1 = SlowRuntimeStub::ToJSTaggedValueWithInt32(thread, right); + INTERPRETER_RETURN_IF_ABRUPT(taggedNumber1); + opNumber0 = taggedNumber0.GetInt(); + opNumber1 = taggedNumber1.GetInt(); + } + // NOLINT(hicpp-signed-bitwise) + auto ret = static_cast(opNumber0) ^ static_cast(opNumber1); + SET_ACC(JSTaggedValue(static_cast(ret))) + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DELOBJPROP_IMM8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + uint16_t v1 = READ_INST_8_2(); + LOG_INST() << "intrinsics::delobjprop" + << " v0" << v0 << " v1" << v1; + + JSTaggedValue obj = GET_VREG_VALUE(v0); + JSTaggedValue prop = GET_VREG_VALUE(v1); + JSTaggedValue res = SlowRuntimeStub::DelObjProp(thread, obj, prop); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DEFINEGLOBALVAR_IMM8_V8_V8) { + // it38 Unimplement + LOG_INST() << "-------------"; + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DEFINELOCALVAR_IMM8_V8_V8) { + // it38 Unimplement + LOG_INST() << "-------------"; + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DEFINEFUNCEXPR_IMM8_V8_V8) { + // it38 Unimplement + LOG_INST() << "-------------"; + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DEFINEFUNCDYN_IMM8_ID16_V8) { + uint16_t methodId = READ_INST_16_1(); + uint16_t v0 = READ_INST_8_3(); + LOG_INST() << "intrinsics::definefuncDyn" + << " v" << v0; + JSFunction *result = JSFunction::Cast(constpool->GetObjectFromCache(methodId).GetTaggedObject()); + ASSERT(result != nullptr); + if (result->IsResolved()) { + auto res = SlowRuntimeStub::DefinefuncDyn(thread, result); + INTERPRETER_RETURN_IF_ABRUPT(res); + result = JSFunction::Cast(res.GetTaggedObject()); + result->SetConstantPool(thread, JSTaggedValue(constpool)); + } else { + result->SetResolved(thread); + } + + JSTaggedValue envHandle = GET_VREG_VALUE(v0); + result->SetLexicalEnv(thread, envHandle); + SET_ACC(JSTaggedValue(result)) + + DISPATCH(BytecodeInstruction::Format::IMM8_ID16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DEFINENCFUNCDYN_IMM8_ID16_V8) { + uint16_t methodId = READ_INST_16_1(); + uint16_t v0 = READ_INST_8_3(); + JSTaggedValue homeObject = GET_ACC(); + LOG_INST() << "intrinsics::definencfuncDyn" + << " v" << v0; + JSFunction *result = JSFunction::Cast(constpool->GetObjectFromCache(methodId).GetTaggedObject()); + ASSERT(result != nullptr); + if (result->IsResolved()) { + auto res = SlowRuntimeStub::DefineNCFuncDyn(thread, result); + INTERPRETER_RETURN_IF_ABRUPT(res); + result = JSFunction::Cast(res.GetTaggedObject()); + result->SetConstantPool(thread, JSTaggedValue(constpool)); + } else { + result->SetResolved(thread); + } + JSTaggedValue env = GET_VREG_VALUE(v0); + result->SetLexicalEnv(thread, env); + result->SetHomeObject(thread, homeObject); + SET_ACC(JSTaggedValue(result)); + + DISPATCH(BytecodeInstruction::Format::IMM8_ID16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DEFINEMETHOD_IMM8_ID16_V8) { + uint16_t methodId = READ_INST_16_1(); + uint16_t v0 = READ_INST_8_3(); + JSTaggedValue homeObject = GET_ACC(); + LOG_INST() << "intrinsics::definemethod" + << " v" << v0; + JSFunction *result = JSFunction::Cast(constpool->GetObjectFromCache(methodId).GetTaggedObject()); + ASSERT(result != nullptr); + if (result->IsResolved()) { + auto res = SlowRuntimeStub::DefineMethod(thread, result, homeObject); + INTERPRETER_RETURN_IF_ABRUPT(res); + result = JSFunction::Cast(res.GetTaggedObject()); + result->SetConstantPool(thread, JSTaggedValue(constpool)); + } else { + result->SetHomeObject(thread, homeObject); + result->SetResolved(thread); + } + + JSTaggedValue taggedCurEnv = GET_VREG_VALUE(v0); + result->SetLexicalEnv(thread, taggedCurEnv); + SET_ACC(JSTaggedValue(result)); + + DISPATCH(BytecodeInstruction::Format::IMM8_ID16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_NEWOBJDYNRANGE_IMM8_IMM16_V8) { + uint16_t numArgs = READ_INST_16_1(); + uint16_t firstArgRegIdx = READ_INST_8_3(); + LOG_INST() << "intrinsics::newobjDynrange " << numArgs << " v" << firstArgRegIdx; + + constexpr uint16_t firstArgOffset = 2; + JSTaggedValue func = GET_VREG_VALUE(firstArgRegIdx); + JSTaggedValue newTarget = GET_VREG_VALUE(firstArgRegIdx + 1); + // Exclude func and newTarget + uint16_t firstArgIdx = firstArgRegIdx + firstArgOffset; + uint16_t length = numArgs - firstArgOffset; + + JSTaggedValue res = SlowRuntimeStub::NewObjDynRange(thread, func, newTarget, firstArgIdx, length); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_REFEQDYN_IMM8_V8_V8) { + // it38 Unimplement + LOG_INST() << "-------------"; + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_EXPDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::expdyn" + << " v" << v0; + JSTaggedValue base = GET_VREG_VALUE(v0); + JSTaggedValue exponent = GET_ACC(); + if (base.IsNumber() && exponent.IsNumber()) { + // fast path + double doubleBase = base.IsInt() ? base.GetInt() : base.GetDouble(); + double doubleExponent = exponent.IsInt() ? exponent.GetInt() : exponent.GetDouble(); + if (std::abs(doubleBase) == 1 && std::isinf(doubleExponent)) { + SET_ACC(JSTaggedValue(base::NAN_VALUE)); + } + if ((doubleBase == 0 && + ((bit_cast(doubleBase)) & base::DOUBLE_SIGN_MASK) == base::DOUBLE_SIGN_MASK) && + std::isfinite(doubleExponent) && base::NumberHelper::TruncateDouble(doubleExponent) == doubleExponent && + base::NumberHelper::TruncateDouble(doubleExponent / 2) + base::HALF == // 2 : half + (doubleExponent / 2)) { // 2 : half + if (doubleExponent > 0) { + SET_ACC(JSTaggedValue(-0.0)); + } + if (doubleExponent < 0) { + SET_ACC(JSTaggedValue(-base::POSITIVE_INFINITY)); + } + } + SET_ACC(JSTaggedValue(std::pow(doubleBase, doubleExponent))); + } else { + // slow path + JSTaggedValue res = SlowRuntimeStub::ExpDyn(thread, base, exponent); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_CALLRUNTIMERANGE_IMM8_V8_V8) { + // it38 Unimplement + LOG_INST() << "-------------"; + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_ISINDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::isindyn" + << " v" << v0; + JSTaggedValue prop = GET_VREG_VALUE(v0); + JSTaggedValue obj = GET_ACC(); + JSTaggedValue res = SlowRuntimeStub::IsInDyn(thread, prop, obj); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_INSTANCEOFDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::instanceofdyn" + << " v" << v0; + JSTaggedValue obj = GET_VREG_VALUE(v0); + JSTaggedValue target = GET_ACC(); + JSTaggedValue res = SlowRuntimeStub::InstanceofDyn(thread, obj, target); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STRICTNOTEQDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::strictnoteq" + << " v" << v0; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = GET_ACC(); + bool res = FastRuntimeStub::FastStrictEqual(left, right); + SET_ACC(JSTaggedValue(!res)); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STRICTEQDYN_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::stricteq" + << " v" << v0; + JSTaggedValue left = GET_VREG_VALUE(v0); + JSTaggedValue right = GET_ACC(); + bool res = FastRuntimeStub::FastStrictEqual(left, right); + SET_ACC(JSTaggedValue(res)); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDLEXVARDYN_IMM8_IMM16_IMM16) { + uint16_t level = READ_INST_16_1(); + uint16_t slot = READ_INST_16_3(); + + LOG_INST() << "intrinsics::ldlexvardyn" + << " level:" << level << " slot:" << slot; + FrameState *state = GET_FRAME(sp); + JSTaggedValue currentLexenv = state->env; + JSTaggedValue env(currentLexenv); + for (int i = 0; i < level; i++) { + JSTaggedValue taggedParentEnv = LexicalEnv::Cast(env.GetTaggedObject())->GetParentEnv(); + ASSERT(!taggedParentEnv.IsUndefined()); + env = taggedParentEnv; + } + SET_ACC(LexicalEnv::Cast(env.GetTaggedObject())->GetProperties(slot)); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_IMM16); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STLEXVARDYN_IMM8_IMM16_IMM16_V8) { + uint16_t level = READ_INST_16_1(); + uint16_t slot = READ_INST_16_3(); + uint16_t v0 = READ_INST_8_5(); + LOG_INST() << "intrinsics::stlexvardyn" + << " level:" << level << " slot:" << slot << " v" << v0; + + JSTaggedValue value = GET_VREG_VALUE(v0); + FrameState *state = GET_FRAME(sp); + JSTaggedValue env = state->env; + for (int i = 0; i < level; i++) { + JSTaggedValue taggedParentEnv = LexicalEnv::Cast(env.GetTaggedObject())->GetParentEnv(); + ASSERT(!taggedParentEnv.IsUndefined()); + env = taggedParentEnv; + } + LexicalEnv::Cast(env.GetTaggedObject())->SetProperties(thread, slot, value); + + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_IMM16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_NEWLEXENVDYN_IMM8_IMM16) { + uint16_t numVars = READ_INST_16_1(); + LOG_INST() << "intrinsics::newlexenvdyn" + << " imm " << numVars; + + JSTaggedValue res = FastRuntimeStub::NewLexicalEnvDyn(thread, factory, numVars); + if (res.IsHole()) { + res = SlowRuntimeStub::NewLexicalEnvDyn(thread, numVars); + INTERPRETER_RETURN_IF_ABRUPT(res); + } + SET_ACC(res); + GET_FRAME(sp)->env = res; + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16); + } + HANDLE_OPCODE(HANDLE_BUILTIN_POPLEXENVDYN_IMM8) { + FrameState *state = GET_FRAME(sp); + JSTaggedValue currentLexenv = state->env; + JSTaggedValue parentLexenv = LexicalEnv::Cast(currentLexenv.GetTaggedObject())->GetParentEnv(); + GET_FRAME(sp)->env = parentLexenv; + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_CREATEITERRESULTOBJ_IMM8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + uint16_t v1 = READ_INST_8_2(); + LOG_INST() << "intrinsics::createiterresultobj" + << " v" << v0 << " v" << v1; + JSTaggedValue value = GET_VREG_VALUE(v0); + JSTaggedValue flag = GET_VREG_VALUE(v1); + JSTaggedValue res = SlowRuntimeStub::CreateIterResultObj(thread, value, flag); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_SUSPENDGENERATOR_IMM8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + uint16_t v1 = READ_INST_8_2(); + LOG_INST() << "intrinsics::suspendgenerator" + << " v" << v0 << " v" << v1; + JSTaggedValue genObj = GET_VREG_VALUE(v0); + JSTaggedValue value = GET_VREG_VALUE(v1); + // suspend will record bytecode offset + SAVE_PC(); + SAVE_ACC(); + JSTaggedValue res = SlowRuntimeStub::SuspendGenerator(thread, genObj, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + + FrameState *state = GET_FRAME(sp); + [[maybe_unused]] auto fistPC = state->method->GetInstructions(); + UPDATE_HOTNESS_COUNTER(thread, sp, -(pc - fistPC)); + LOG(DEBUG, INTERPRETER) << "Exit: SuspendGenerator " << std::hex << reinterpret_cast(sp) << " " + << std::hex << reinterpret_cast(state->pc); + sp = state->prev; + ASSERT(sp != nullptr); + FrameState *prevState = GET_FRAME(sp); + pc = prevState->pc; + + // break frame + if (pc == nullptr) { + state->acc = acc; + return; + } + thread->SetCurrentSPFrame(sp); + constpool = prevState->constpool; + + size_t jumpSize = GetJumpSizeAfterCall(pc); + DISPATCH_OFFSET(jumpSize); + } + HANDLE_OPCODE(HANDLE_BUILTIN_ASYNCFUNCTIONAWAITUNCAUGHT_IMM8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + uint16_t v1 = READ_INST_8_2(); + LOG_INST() << "intrinsics::asyncfunctionawaituncaught" + << " v" << v0 << " v" << v1; + JSTaggedValue asyncFuncObj = GET_VREG_VALUE(v0); + JSTaggedValue value = GET_VREG_VALUE(v1); + JSTaggedValue res = SlowRuntimeStub::AsyncFunctionAwaitUncaught(thread, asyncFuncObj, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_ASYNCFUNCTIONRESOLVE_IMM8_V8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + [[maybe_unused]] uint16_t v1 = READ_INST_8_2(); + uint16_t v2 = READ_INST_8_3(); + LOG_INST() << "intrinsics::asyncfunctionresolve" + << " v" << v0 << " v" << v1 << " v" << v2; + + JSTaggedValue asyncFuncObj = GET_VREG_VALUE(v0); + JSTaggedValue value = GET_VREG_VALUE(v2); + JSTaggedValue res = SlowRuntimeStub::AsyncFunctionResolveOrReject(thread, asyncFuncObj, value, true); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_ASYNCFUNCTIONREJECT_IMM8_V8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + [[maybe_unused]] uint16_t v1 = READ_INST_8_2(); + uint16_t v2 = READ_INST_8_3(); + LOG_INST() << "intrinsics::asyncfunctionreject" + << " v" << v0 << " v" << v1 << " v" << v2; + + JSTaggedValue asyncFuncObj = GET_VREG_VALUE(v0); + JSTaggedValue value = GET_VREG_VALUE(v2); + SAVE_ACC(); + JSTaggedValue res = SlowRuntimeStub::AsyncFunctionResolveOrReject(thread, asyncFuncObj, value, false); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_NEWOBJSPREADDYN_IMM8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + uint16_t v1 = READ_INST_8_2(); + LOG_INST() << "intrinsic::newobjspearddyn" + << " v" << v0 << " v" << v1; + JSTaggedValue func = GET_VREG_VALUE(v0); + JSTaggedValue newTarget = GET_VREG_VALUE(v1); + JSTaggedValue array = GET_ACC(); + JSTaggedValue res = SlowRuntimeStub::NewObjSpreadDyn(thread, func, newTarget, array); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_THROWUNDEFINEDIFHOLE_IMM8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + uint16_t v1 = READ_INST_8_2(); + LOG_INST() << "intrinsic::throwundefinedifhole" + << " v" << v0 << " v" << v1; + JSTaggedValue hole = GET_VREG_VALUE(v0); + if (!hole.IsHole()) { + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + JSTaggedValue obj = GET_VREG_VALUE(v1); + ASSERT(obj.IsString()); + SlowRuntimeStub::ThrowUndefinedIfHole(thread, obj); + INTERPRETER_GOTO_EXCEPTION_HANDLER(); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STOWNBYNAME_IMM8_ID32_IMM16_V8) { + uint32_t stringId = READ_INST_32_1(); + uint32_t v0 = ReadU8(pc, 1); + LOG_INST() << "intrinsics::stownbyname " + << "v" << v0 << " stringId:" << stringId; + + JSTaggedValue receiver = GET_VREG_VALUE(v0); + if (receiver.IsJSObject() && !receiver.IsClassConstructor() && !receiver.IsClassPrototype()) { + JSTaggedValue propKey = constpool->GetObjectFromCache(stringId); + JSTaggedValue value = GET_ACC(); + // fast path + SAVE_ACC(); + JSTaggedValue res = FastRuntimeStub::SetPropertyByName(thread, receiver, propKey, value); + if (!res.IsHole()) { + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16_V8); + } + RESTORE_ACC(); + } + + SAVE_ACC(); + receiver = GET_VREG_VALUE(v0); // Maybe moved by GC + auto propKey = constpool->GetObjectFromCache(stringId); // Maybe moved by GC + auto value = GET_ACC(); // Maybe moved by GC + JSTaggedValue res = SlowRuntimeStub::StOwnByName(thread, receiver, propKey, value); + RESTORE_ACC(); + INTERPRETER_RETURN_IF_ABRUPT(res); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_CREATEEMPTYARRAY_IMM8) { + LOG_INST() << "intrinsics::createemptyarray"; + JSTaggedValue res = SlowRuntimeStub::CreateEmptyArray(thread, factory, globalEnv); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_CREATEEMPTYOBJECT_IMM8) { + LOG_INST() << "intrinsics::createemptyobject"; + JSTaggedValue res = SlowRuntimeStub::CreateEmptyObject(thread, factory, globalEnv); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_CREATEOBJECTWITHBUFFER_IMM8_IMM16) { + uint16_t imm = READ_INST_16_1(); + LOG_INST() << "intrinsics::createobjectwithbuffer" + << " imm:" << imm; + JSObject *result = JSObject::Cast(constpool->GetObjectFromCache(imm).GetTaggedObject()); + + JSTaggedValue res = SlowRuntimeStub::CreateObjectWithBuffer(thread, factory, result); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16); + } + HANDLE_OPCODE(HANDLE_BUILTIN_SETOBJECTWITHPROTO_IMM8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + uint16_t v1 = READ_INST_8_2(); + LOG_INST() << "intrinsics::setobjectwithproto" + << " v" << v0 << " v" << v1; + JSTaggedValue proto = GET_VREG_VALUE(v0); + JSTaggedValue obj = GET_VREG_VALUE(v1); + SAVE_ACC(); + JSTaggedValue res = SlowRuntimeStub::SetObjectWithProto(thread, proto, obj); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_CREATEARRAYWITHBUFFER_IMM8_IMM16) { + uint16_t imm = READ_INST_16_1(); + LOG_INST() << "intrinsics::createarraywithbuffer" + << " imm:" << imm; + JSArray *result = JSArray::Cast(constpool->GetObjectFromCache(imm).GetTaggedObject()); + JSTaggedValue res = SlowRuntimeStub::CreateArrayWithBuffer(thread, factory, result); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16); + } + HANDLE_OPCODE(HANDLE_BUILTIN_IMPORTMODULE_IMM8_ID32) { + uint32_t stringId = READ_INST_32_1(); + auto prop = constpool->GetObjectFromCache(stringId); + + LOG_INST() << "intrinsics::importmodule " + << "stringId:" << stringId << ", " << ConvertToString(EcmaString::Cast(prop.GetTaggedObject())); + + JSTaggedValue moduleRef = SlowRuntimeStub::ImportModule(thread, prop); + SET_ACC(moduleRef); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STMODULEVAR_IMM8_ID32) { + uint32_t stringId = READ_INST_32_1(); + auto prop = constpool->GetObjectFromCache(stringId); + + LOG_INST() << "intrinsics::stmodulevar " + << "stringId:" << stringId << ", " << ConvertToString(EcmaString::Cast(prop.GetTaggedObject())); + JSTaggedValue value = GET_ACC(); + + SAVE_ACC(); + SlowRuntimeStub::StModuleVar(thread, prop, value); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32); + } + HANDLE_OPCODE(HANDLE_BUILTIN_COPYMODULE_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + JSTaggedValue srcModule = GET_VREG_VALUE(v0); + + LOG_INST() << "intrinsics::copymodule "; + + SAVE_ACC(); + SlowRuntimeStub::CopyModule(thread, srcModule); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDMODVARBYNAME_IMM8_ID32_IMM16_V8) { + uint32_t stringId = READ_INST_32_1(); + uint32_t v0 = ReadU8(pc, 1); + + JSTaggedValue itemName = constpool->GetObjectFromCache(stringId); + JSTaggedValue moduleObj = GET_VREG_VALUE(v0); + LOG_INST() << "intrinsics::ldmodvarbyname " + << "string_id:" << stringId << ", " + << "itemName: " << ConvertToString(EcmaString::Cast(itemName.GetTaggedObject())); + + JSTaggedValue moduleVar = SlowRuntimeStub::LdModvarByName(thread, moduleObj, itemName); + SET_ACC(moduleVar); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_GETTEMPLATEOBJECT_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsic::gettemplateobject" + << " v" << v0; + + JSTaggedValue literal = GET_VREG_VALUE(v0); + JSTaggedValue res = SlowRuntimeStub::GetTemplateObject(thread, literal); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_GETNEXTPROPNAME_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsic::getnextpropname" + << " v" << v0; + JSTaggedValue iter = GET_VREG_VALUE(v0); + JSTaggedValue res = SlowRuntimeStub::GetNextPropName(thread, iter); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_COPYDATAPROPERTIES_IMM8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + uint16_t v1 = READ_INST_8_2(); + LOG_INST() << "intrinsic::copydataproperties" + << " v" << v0 << " v" << v1; + JSTaggedValue dst = GET_VREG_VALUE(v0); + JSTaggedValue src = GET_VREG_VALUE(v1); + JSTaggedValue res = SlowRuntimeStub::CopyDataProperties(thread, dst, src); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STOWNBYINDEX_IMM8_IMM16_V8_V8) { + uint32_t v0 = READ_INST_8_3(); + uint32_t v1 = READ_INST_8_4(); + LOG_INST() << "intrinsics::stownbyindex" + << " v" << v0 << " v" << v1; + JSTaggedValue receiver = GET_VREG_VALUE(v0); + JSTaggedValue index = GET_VREG_VALUE(v1); + // fast path + if (receiver.IsHeapObject() && !receiver.IsClassConstructor() && !receiver.IsClassPrototype()) { + SAVE_ACC(); + JSTaggedValue value = GET_ACC(); + // fast path + JSTaggedValue res = + FastRuntimeStub::SetPropertyByIndex(thread, receiver, index.GetArrayLength(), value); + if (!res.IsHole()) { + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + RESTORE_ACC(); + } + SAVE_ACC(); + receiver = GET_VREG_VALUE(v0); // Maybe moved by GC + auto value = GET_ACC(); // Maybe moved by GC + JSTaggedValue res = SlowRuntimeStub::StOwnByIndex(thread, receiver, index, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STOWNBYVALUE_IMM8_IMM16_V8_V8) { + uint32_t v0 = READ_INST_8_3(); + uint32_t v1 = READ_INST_8_4(); + LOG_INST() << "intrinsics::stownbyvalue" + << " v" << v0 << " v" << v1; + + JSTaggedValue receiver = GET_VREG_VALUE(v0); + if (receiver.IsHeapObject() && !receiver.IsClassConstructor() && !receiver.IsClassPrototype()) { + SAVE_ACC(); + JSTaggedValue propKey = GET_VREG_VALUE(v1); + JSTaggedValue value = GET_ACC(); + // fast path + JSTaggedValue res = FastRuntimeStub::SetPropertyByValue(thread, receiver, propKey, value); + + // SetPropertyByValue maybe gc need update the value + RESTORE_ACC(); + propKey = GET_VREG_VALUE(v1); + value = GET_ACC(); + if (!res.IsHole()) { + INTERPRETER_RETURN_IF_ABRUPT(res); + if (value.IsJSFunction()) { + JSFunction::SetFunctionNameNoPrefix(thread, JSFunction::Cast(value.GetTaggedObject()), propKey); + } + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + } + + // slow path + SAVE_ACC(); + receiver = GET_VREG_VALUE(v0); // Maybe moved by GC + auto propKey = GET_VREG_VALUE(v1); // Maybe moved by GC + auto value = GET_ACC(); // Maybe moved by GC + JSTaggedValue res = SlowRuntimeStub::StOwnByValue(thread, receiver, propKey, value); + RESTORE_ACC(); + INTERPRETER_RETURN_IF_ABRUPT(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_CREATEOBJECTWITHEXCLUDEDKEYS_IMM8_IMM16_V8_V8) { + uint16_t numKeys = READ_INST_16_1(); + uint16_t v0 = READ_INST_8_3(); + uint16_t firstArgRegIdx = READ_INST_8_4(); + LOG_INST() << "intrinsics::createobjectwithexcludedkeys " << numKeys << " v" << firstArgRegIdx; + + JSTaggedValue obj = GET_VREG_VALUE(v0); + + JSTaggedValue res = SlowRuntimeStub::CreateObjectWithExcludedKeys(thread, numKeys, obj, firstArgRegIdx); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DEFINEGENERATORFUNC_IMM8_ID16_V8) { + uint16_t methodId = READ_INST_16_1(); + uint16_t v0 = READ_INST_8_3(); + LOG_INST() << "define gengerator function" + << " v" << v0; + JSFunction *result = JSFunction::Cast(constpool->GetObjectFromCache(methodId).GetTaggedObject()); + ASSERT(result != nullptr); + if (result->IsResolved()) { + auto res = SlowRuntimeStub::DefineGeneratorFunc(thread, result); + INTERPRETER_RETURN_IF_ABRUPT(res); + result = JSFunction::Cast(res.GetTaggedObject()); + result->SetConstantPool(thread, JSTaggedValue(constpool)); + } else { + result->SetResolved(thread); + } + JSTaggedValue env = GET_VREG_VALUE(v0); + result->SetLexicalEnv(thread, env); + SET_ACC(JSTaggedValue(result)) + DISPATCH(BytecodeInstruction::Format::IMM8_ID16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DEFINEASYNCFUNC_IMM8_ID16_V8) { + uint16_t methodId = READ_INST_16_1(); + uint16_t v0 = READ_INST_8_3(); + LOG_INST() << "define async function" + << " v" << v0; + JSFunction *result = JSFunction::Cast(constpool->GetObjectFromCache(methodId).GetTaggedObject()); + ASSERT(result != nullptr); + if (result->IsResolved()) { + auto res = SlowRuntimeStub::DefineAsyncFunc(thread, result); + INTERPRETER_RETURN_IF_ABRUPT(res); + result = JSFunction::Cast(res.GetTaggedObject()); + result->SetConstantPool(thread, JSTaggedValue(constpool)); + } else { + result->SetResolved(thread); + } + JSTaggedValue env = GET_VREG_VALUE(v0); + result->SetLexicalEnv(thread, env); + SET_ACC(JSTaggedValue(result)) + DISPATCH(BytecodeInstruction::Format::IMM8_ID16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDHOLE_IMM8) { + LOG_INST() << "intrinsic::ldhole"; + SET_ACC(JSTaggedValue::Hole()); + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_COPYRESTARGS_IMM8_IMM16) { + uint16_t restIdx = READ_INST_16_1(); + LOG_INST() << "intrinsics::copyrestargs" + << " index: " << restIdx; + + FrameState *state = GET_FRAME(sp); + uint32_t numVregs = state->method->GetNumVregs(); + // Exclude func, newTarget and "this" + int32_t actualNumArgs = state->numActualArgs - NUM_MANDATORY_JSFUNC_ARGS; + int32_t tmp = actualNumArgs - restIdx; + uint32_t restNumArgs = (tmp > 0) ? tmp : 0; + uint32_t startIdx = numVregs + NUM_MANDATORY_JSFUNC_ARGS + restIdx; + + JSTaggedValue res = SlowRuntimeStub::CopyRestArgs(thread, sp, restNumArgs, startIdx); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16); + } + HANDLE_OPCODE(HANDLE_BUILTIN_TRYLDGLOBALBYVALUE_IMM8_IMM16_V8) { + // replaced by ldobjbyindex, not need + LOG_INST() << "-------------"; + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DEFINEGETTERSETTERBYVALUE_IMM8_V8_V8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + uint16_t v1 = READ_INST_8_2(); + uint16_t v2 = READ_INST_8_3(); + uint16_t v3 = READ_INST_8_4(); + LOG_INST() << "intrinsics::definegettersetterbyvalue" + << " v" << v0 << " v" << v1 << " v" << v2 << " v" << v3; + + JSTaggedValue obj = GET_VREG_VALUE(v0); + JSTaggedValue prop = GET_VREG_VALUE(v1); + JSTaggedValue getter = GET_VREG_VALUE(v2); + JSTaggedValue setter = GET_VREG_VALUE(v3); + JSTaggedValue flag = GET_ACC(); + JSTaggedValue res = + SlowRuntimeStub::DefineGetterSetterByValue(thread, obj, prop, getter, setter, flag.ToBoolean()); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_TRYSTGLOBALBYVALUE_IMM8_IMM16_V8) { + // replaced by stobjbyindex, not need + LOG_INST() << "-------------"; + UNREACHABLE(); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDOBJBYINDEX_IMM8_IMM16_V8_V8) { + uint16_t v0 = READ_INST_8_3(); + uint16_t v1 = READ_INST_8_4(); + LOG_INST() << "intrinsics::ldobjbyindex" + << " v" << v0 << " v" << v1; + + JSTaggedValue receiver = GET_VREG_VALUE(v0); + JSTaggedValue idx = GET_VREG_VALUE(v1); + ASSERT(!(idx.IsDouble() && idx.GetDouble() >= JSObject::MAX_ELEMENT_INDEX)); + // fast path + if (LIKELY(receiver.IsHeapObject())) { + JSTaggedValue res = FastRuntimeStub::GetPropertyByIndex(thread, receiver, idx.GetArrayLength()); + if (!res.IsHole()) { + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + } + // not meet fast condition or fast path return hole, walk slow path + // slow stub not need receiver + JSTaggedValue res = SlowRuntimeStub::LdObjByIndex(thread, receiver, idx, false, JSTaggedValue::Undefined()); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STOBJBYINDEX_IMM8_IMM16_V8_V8) { + uint16_t v0 = READ_INST_8_3(); + uint16_t v1 = READ_INST_8_4(); + LOG_INST() << "intrinsics::stobjbyindex" + << " v" << v0 << " v" << v1; + + JSTaggedValue receiver = GET_VREG_VALUE(v0); + JSTaggedValue index = GET_VREG_VALUE(v1); + if (receiver.IsHeapObject()) { + SAVE_ACC(); + JSTaggedValue value = GET_ACC(); + // fast path + JSTaggedValue res = FastRuntimeStub::SetPropertyByIndex(thread, receiver, index.GetArrayLength(), value); + if (!res.IsHole()) { + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + RESTORE_ACC(); + } + // slow path + SAVE_ACC(); + receiver = GET_VREG_VALUE(v0); // Maybe moved by GC + JSTaggedValue value = GET_ACC(); // Maybe moved by GC + JSTaggedValue res = SlowRuntimeStub::StObjByIndex(thread, receiver, index, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDOBJBYVALUE_IMM8_IMM16_V8_V8) { + uint32_t v0 = READ_INST_8_3(); + uint32_t v1 = READ_INST_8_4(); + LOG_INST() << "intrinsics::Ldobjbyvalue" + << " v" << v0 << " v" << v1; + + JSTaggedValue receiver = GET_VREG_VALUE(v0); + JSTaggedValue propKey = GET_VREG_VALUE(v1); + +#if ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(sp); + if (!profileTypeInfo.IsUndefined()) { + uint16_t slotId = READ_INST_16_1(); + JSTaggedValue res = ICRuntimeStub::LoadICByValue(thread, + ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject()), + receiver, propKey, slotId); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } +#endif + // fast path + if (LIKELY(receiver.IsHeapObject())) { + JSTaggedValue res = FastRuntimeStub::GetPropertyByValue(thread, receiver, propKey); + if (!res.IsHole()) { + ASSERT(!res.IsAccessor()); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + } + // slow path + JSTaggedValue res = SlowRuntimeStub::LdObjByValue(thread, receiver, propKey, false, JSTaggedValue::Undefined()); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STOBJBYVALUE_IMM8_IMM16_V8_V8) { + uint32_t v0 = READ_INST_8_3(); + uint32_t v1 = READ_INST_8_4(); + + LOG_INST() << "intrinsics::stobjbyvalue" + << " v" << v0 << " v" << v1; + + JSTaggedValue receiver = GET_VREG_VALUE(v0); +#if ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(sp); + if (!profileTypeInfo.IsUndefined()) { + uint16_t slotId = READ_INST_16_1(); + SAVE_ACC(); + JSTaggedValue propKey = GET_VREG_VALUE(v1); + JSTaggedValue value = GET_ACC(); + JSTaggedValue res = ICRuntimeStub::StoreICByValue(thread, + ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject()), + receiver, propKey, value, slotId); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } +#endif + if (receiver.IsHeapObject()) { + SAVE_ACC(); + JSTaggedValue propKey = GET_VREG_VALUE(v1); + JSTaggedValue value = GET_ACC(); + // fast path + JSTaggedValue res = FastRuntimeStub::SetPropertyByValue(thread, receiver, propKey, value); + if (!res.IsHole()) { + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + RESTORE_ACC(); + } + { + // slow path + SAVE_ACC(); + receiver = GET_VREG_VALUE(v0); // Maybe moved by GC + JSTaggedValue propKey = GET_VREG_VALUE(v1); // Maybe moved by GC + JSTaggedValue value = GET_ACC(); // Maybe moved by GC + JSTaggedValue res = SlowRuntimeStub::StObjByValue(thread, receiver, propKey, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + } + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDSUPERBYVALUE_IMM8_IMM16_V8_V8) { + uint32_t v0 = READ_INST_8_3(); + uint32_t v1 = READ_INST_8_4(); + LOG_INST() << "intrinsics::Ldsuperbyvalue" + << " v" << v0 << " v" << v1; + + JSTaggedValue receiver = GET_VREG_VALUE(v0); + JSTaggedValue propKey = GET_VREG_VALUE(v1); + + // slow path + JSTaggedValue thisFunc = GetThisFunction(sp); + JSTaggedValue res = SlowRuntimeStub::LdSuperByValue(thread, receiver, propKey, thisFunc); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STSUPERBYVALUE_IMM8_IMM16_V8_V8) { + uint32_t v0 = READ_INST_8_3(); + uint32_t v1 = READ_INST_8_4(); + + LOG_INST() << "intrinsics::stsuperbyvalue" + << " v" << v0 << " v" << v1; + JSTaggedValue receiver = GET_VREG_VALUE(v0); + JSTaggedValue propKey = GET_VREG_VALUE(v1); + JSTaggedValue value = GET_ACC(); + + // slow path + SAVE_ACC(); + JSTaggedValue thisFunc = GetThisFunction(sp); + JSTaggedValue res = SlowRuntimeStub::StSuperByValue(thread, receiver, propKey, value, thisFunc); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_TRYLDGLOBALBYNAME_IMM8_ID32_IMM16) { + uint32_t stringId = READ_INST_32_1(); + auto prop = constpool->GetObjectFromCache(stringId); + + LOG_INST() << "intrinsics::tryldglobalbyname " + << "stringId:" << stringId << ", " << ConvertToString(EcmaString::Cast(prop.GetTaggedObject())); + +#if ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(sp); + if (!profileTypeInfo.IsUndefined()) { + uint16_t slotId = READ_INST_16_5(); + JSTaggedValue res = ICRuntimeStub::LoadGlobalICByName(thread, + ProfileTypeInfo::Cast( + profileTypeInfo.GetTaggedObject()), + globalObj, prop, slotId); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16); + } +#endif + + bool found = false; + JSTaggedValue result = FastRuntimeStub::GetGlobalOwnProperty(globalObj, prop, &found); + if (found) { + SET_ACC(result); + } else { + // slow path + JSTaggedValue res = SlowRuntimeStub::TryLdGlobalByName(thread, globalObj, prop); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16); + } + HANDLE_OPCODE(HANDLE_BUILTIN_TRYSTGLOBALBYNAME_IMM8_ID32_IMM16) { + uint32_t stringId = READ_INST_32_1(); + JSTaggedValue propKey = constpool->GetObjectFromCache(stringId); + LOG_INST() << "intrinsics::trystglobalbyname" + << " stringId:" << stringId << ", " << ConvertToString(EcmaString::Cast(propKey.GetTaggedObject())); + +#if ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(sp); + if (!profileTypeInfo.IsUndefined()) { + uint16_t slotId = READ_INST_16_5(); + JSTaggedValue value = GET_ACC(); + SAVE_ACC(); + JSTaggedValue res = ICRuntimeStub::StoreGlobalICByName(thread, + ProfileTypeInfo::Cast( + profileTypeInfo.GetTaggedObject()), + globalObj, propKey, value, slotId); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16); + } +#endif + + bool found = false; + FastRuntimeStub::GetGlobalOwnProperty(globalObj, propKey, &found); + if (!found) { + // slow path will throw exception + JSTaggedValue res = SlowRuntimeStub::TryStGlobalByName(thread, propKey); + INTERPRETER_RETURN_IF_ABRUPT(res); + } else { + JSTaggedValue value = GET_ACC(); + SAVE_ACC(); + JSTaggedValue res = SlowRuntimeStub::StGlobalVar(thread, propKey, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + } + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDGLOBALVAR_IMM8_ID32_IMM16) { + uint32_t stringId = READ_INST_32_1(); + JSTaggedValue propKey = constpool->GetObjectFromCache(stringId); + +#if ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(sp); + if (!profileTypeInfo.IsUndefined()) { + uint16_t slotId = READ_INST_16_5(); + JSTaggedValue res = ICRuntimeStub::LoadGlobalICByName(thread, + ProfileTypeInfo::Cast( + profileTypeInfo.GetTaggedObject()), + globalObj, propKey, slotId); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16); + } +#endif + bool found = false; + JSTaggedValue result = FastRuntimeStub::GetGlobalOwnProperty(globalObj, propKey, &found); + if (found) { + SET_ACC(result); + } else { + // slow path + JSTaggedValue res = SlowRuntimeStub::LdGlobalVar(thread, globalObj, propKey); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + } + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDOBJBYNAME_IMM8_ID32_IMM16_V8) { + uint32_t stringId = READ_INST_32_1(); + uint32_t v0 = ReadU8(pc, 1); + JSTaggedValue receiver = GET_VREG_VALUE(v0); + JSTaggedValue propKey = constpool->GetObjectFromCache(stringId); + + LOG_INST() << "intrinsics::ldobjbyname " + << "v" << v0 << " stringId:" << stringId << ", " + << ConvertToString(EcmaString::Cast(propKey.GetTaggedObject())) << ", obj:" << receiver.GetRawData(); + +#if ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(sp); + if (!profileTypeInfo.IsUndefined()) { + uint16_t slotId = READ_INST_16_5(); + JSTaggedValue res = ICRuntimeStub::LoadICByName(thread, + ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject()), + receiver, propKey, slotId); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16_V8); + } +#endif + if (LIKELY(receiver.IsHeapObject())) { + // fast path + JSTaggedValue res = FastRuntimeStub::GetPropertyByName(thread, receiver, propKey); + if (!res.IsHole()) { + ASSERT(!res.IsAccessor()); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16_V8); + } + } + // not meet fast condition or fast path return hole, walk slow path + // slow stub not need receiver + JSTaggedValue res = SlowRuntimeStub::LdObjByName(thread, receiver, propKey, false, JSTaggedValue::Undefined()); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STOBJBYNAME_IMM8_ID32_IMM16_V8) { + uint32_t stringId = READ_INST_32_1(); + uint32_t v0 = ReadU8(pc, 1); + LOG_INST() << "intrinsics::stobjbyname " + << "v" << v0 << " stringId:" << stringId; + + JSTaggedValue receiver = GET_VREG_VALUE(v0); +#if ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(sp); + if (!profileTypeInfo.IsUndefined()) { + uint16_t slotId = READ_INST_16_5(); + SAVE_ACC(); + JSTaggedValue propKey = constpool->GetObjectFromCache(stringId); + JSTaggedValue value = GET_ACC(); + JSTaggedValue res = ICRuntimeStub::StoreICByName(thread, + ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject()), + receiver, propKey, value, slotId); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16_V8); + } +#endif + if (receiver.IsHeapObject()) { + JSTaggedValue propKey = constpool->GetObjectFromCache(stringId); + JSTaggedValue value = GET_ACC(); + // fast path + SAVE_ACC(); + JSTaggedValue res = FastRuntimeStub::SetPropertyByName(thread, receiver, propKey, value); + if (!res.IsHole()) { + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16_V8); + } + RESTORE_ACC(); + } + // slow path + SAVE_ACC(); + receiver = GET_VREG_VALUE(v0); // Maybe moved by GC + auto propKey = constpool->GetObjectFromCache(stringId); // Maybe moved by GC + auto value = GET_ACC(); // Maybe moved by GC + JSTaggedValue res = SlowRuntimeStub::StObjByName(thread, receiver, propKey, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDSUPERBYNAME_IMM8_ID32_IMM16_V8) { + uint32_t stringId = READ_INST_32_1(); + uint32_t v0 = ReadU8(pc, 1); + JSTaggedValue obj = GET_VREG_VALUE(v0); + JSTaggedValue propKey = constpool->GetObjectFromCache(stringId); + + LOG_INST() << "intrinsics::ldsuperbyname" + << "v" << v0 << " stringId:" << stringId << ", " + << ConvertToString(EcmaString::Cast(propKey.GetTaggedObject())) << ", obj:" << obj.GetRawData(); + + JSTaggedValue thisFunc = GetThisFunction(sp); + JSTaggedValue res = SlowRuntimeStub::LdSuperByValue(thread, obj, propKey, thisFunc); + + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STSUPERBYNAME_IMM8_ID32_IMM16_V8) { + uint32_t stringId = READ_INST_32_1(); + uint32_t v0 = ReadU8(pc, 1); + + JSTaggedValue obj = GET_VREG_VALUE(v0); + JSTaggedValue propKey = constpool->GetObjectFromCache(stringId); + JSTaggedValue value = GET_ACC(); + + LOG_INST() << "intrinsics::stsuperbyname" + << "v" << v0 << " stringId:" << stringId << ", " + << ConvertToString(EcmaString::Cast(propKey.GetTaggedObject())) << ", obj:" << obj.GetRawData() + << ", value:" << value.GetRawData(); + + // slow path + SAVE_ACC(); + JSTaggedValue thisFunc = GetThisFunction(sp); + JSTaggedValue res = SlowRuntimeStub::StSuperByValue(thread, obj, propKey, value, thisFunc); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STGLOBALVAR_IMM8_ID32_IMM16) { + uint32_t stringId = READ_INST_32_1(); + JSTaggedValue prop = constpool->GetObjectFromCache(stringId); + JSTaggedValue value = GET_ACC(); + + LOG_INST() << "intrinsics::stglobalvar " + << "stringId:" << stringId << ", " << ConvertToString(EcmaString::Cast(prop.GetTaggedObject())) + << ", value:" << value.GetRawData(); +#if ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(sp); + if (!profileTypeInfo.IsUndefined()) { + uint16_t slotId = READ_INST_16_5(); + SAVE_ACC(); + JSTaggedValue res = ICRuntimeStub::StoreGlobalICByName(thread, + ProfileTypeInfo::Cast( + profileTypeInfo.GetTaggedObject()), + globalObj, prop, value, slotId); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16); + } +#endif + SAVE_ACC(); + JSTaggedValue res = SlowRuntimeStub::StGlobalVar(thread, prop, value); + INTERPRETER_RETURN_IF_ABRUPT(res); + RESTORE_ACC(); + DISPATCH(BytecodeInstruction::Format::IMM8_ID32_IMM16); + } + HANDLE_OPCODE(HANDLE_BUILTIN_CREATEGENERATOROBJ_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsics::creategeneratorobj" + << " v" << v0; + JSTaggedValue genFunc = GET_VREG_VALUE(v0); + JSTaggedValue res = SlowRuntimeStub::CreateGeneratorObj(thread, genFunc); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_STARRAYSPREAD_IMM8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + uint16_t v1 = READ_INST_8_2(); + LOG_INST() << "ecmascript::intrinsics::starrayspread" + << " v" << v0 << " v" << v1 << "acc"; + JSTaggedValue dst = GET_VREG_VALUE(v0); + JSTaggedValue index = GET_VREG_VALUE(v1); + JSTaggedValue src = GET_ACC(); + JSTaggedValue res = SlowRuntimeStub::StArraySpread(thread, dst, index, src); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_GETITERATORNEXT_IMM8_V8_V8) { + uint16_t v0 = READ_INST_8_1(); + uint16_t v1 = READ_INST_8_2(); + LOG_INST() << "intrinsic::getiteratornext" + << " v" << v0 << " v" << v1; + JSTaggedValue obj = GET_VREG_VALUE(v0); + JSTaggedValue method = GET_VREG_VALUE(v1); + JSTaggedValue res = SlowRuntimeStub::GetIteratorNext(thread, obj, method); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DEFINECLASSWITHBUFFER_IMM8_ID16_IMM16_V8_V8) { + uint16_t methodId = READ_INST_16_1(); + uint16_t imm = READ_INST_16_3(); + uint16_t v0 = READ_INST_8_5(); + uint16_t v1 = READ_INST_8_6(); + LOG_INST() << "intrinsics::defineclasswithbuffer" + << " method id:" << methodId << " literal id:" << imm << " lexenv: v" << v0 << " parent: v" << v1; + JSFunction *cls = JSFunction::Cast(constpool->GetObjectFromCache(methodId).GetTaggedObject()); + ASSERT(cls != nullptr); + if (cls->IsResolved()) { + auto res = SlowRuntimeStub::NewClassFunc(thread, cls); + INTERPRETER_RETURN_IF_ABRUPT(res); + cls = JSFunction::Cast(res.GetTaggedObject()); + cls->SetConstantPool(thread, JSTaggedValue(constpool)); + } else { + cls->SetResolved(thread); + } + JSTaggedValue lexenv = GET_VREG_VALUE(v0); + cls->SetLexicalEnv(thread, lexenv); + + TaggedArray *literalBuffer = TaggedArray::Cast(constpool->GetObjectFromCache(imm).GetTaggedObject()); + JSTaggedValue proto = GET_VREG_VALUE(v1); + JSTaggedValue res = SlowRuntimeStub::DefineClass(thread, cls, literalBuffer, proto, lexenv, constpool); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_ID16_IMM16_V8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_SUPERCALL_IMM8_IMM16_V8) { + uint16_t range = READ_INST_16_1(); + uint16_t v0 = READ_INST_8_3(); + LOG_INST() << "intrinsics::supercall" + << " range: " << range << " v" << v0; + + FrameState *state = GET_FRAME(sp); + auto numVregs = state->method->GetNumVregs(); + JSTaggedValue thisFunc = GET_ACC(); + JSTaggedValue newTarget = GET_VREG_VALUE(numVregs + 1); + + JSTaggedValue res = SlowRuntimeStub::SuperCall(thread, thisFunc, newTarget, v0, range); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_SUPERCALLSPREAD_IMM8_V8) { + uint16_t v0 = READ_INST_8_1(); + LOG_INST() << "intrinsic::supercallspread" + << " array: v" << v0; + + FrameState *state = GET_FRAME(sp); + auto numVregs = state->method->GetNumVregs(); + JSTaggedValue thisFunc = GET_ACC(); + JSTaggedValue newTarget = GET_VREG_VALUE(numVregs + 1); + JSTaggedValue array = GET_VREG_VALUE(v0); + + JSTaggedValue res = SlowRuntimeStub::SuperCallSpread(thread, thisFunc, newTarget, array); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_V8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_CREATEOBJECTHAVINGMETHOD_IMM8_IMM16) { + uint16_t imm = READ_INST_16_1(); + LOG_INST() << "intrinsics::createobjecthavingmethod" + << " imm:" << imm; + JSObject *result = JSObject::Cast(constpool->GetObjectFromCache(imm).GetTaggedObject()); + JSTaggedValue env = GET_ACC(); + + JSTaggedValue res = SlowRuntimeStub::CreateObjectHavingMethod(thread, factory, result, env, constpool); + INTERPRETER_RETURN_IF_ABRUPT(res); + SET_ACC(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16); + } + HANDLE_OPCODE(HANDLE_BUILTIN_THROWIFSUPERNOTCORRECTCALL_IMM8_IMM16) { + uint16_t imm = READ_INST_16_1(); + JSTaggedValue thisValue = GET_ACC(); + LOG_INST() << "intrinsic::throwifsupernotcorrectcall" + << " imm:" << imm; + JSTaggedValue res = SlowRuntimeStub::ThrowIfSuperNotCorrectCall(thread, imm, thisValue); + INTERPRETER_RETURN_IF_ABRUPT(res); + DISPATCH(BytecodeInstruction::Format::IMM8_IMM16); + } + HANDLE_OPCODE(HANDLE_BUILTIN_LDHOMEOBJECT_IMM8) { + LOG_INST() << "intrinsics::ldhomeobject"; + + JSTaggedValue thisFunc = GetThisFunction(sp); + JSTaggedValue homeObject = JSFunction::Cast(thisFunc.GetTaggedObject())->GetHomeObject(); + + SET_ACC(homeObject); + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(HANDLE_BUILTIN_THROWDELETESUPERPROPERTY_IMM8) { + LOG_INST() << "throwdeletesuperproperty"; + + SlowRuntimeStub::ThrowDeleteSuperProperty(thread); + INTERPRETER_GOTO_EXCEPTION_HANDLER(); + } + HANDLE_OPCODE(HANDLE_BUILTIN_DEBUGGER_IMM8) { + LOG_INST() << "intrinsics::debugger"; + DISPATCH(BytecodeInstruction::Format::IMM8); + } + HANDLE_OPCODE(EXCEPTION_HANDLER) { + auto exception = thread->GetException(); + + EcmaFrameHandler frameHandler(sp); + uint32_t pcOffset = panda_file::INVALID_OFFSET; + for (; frameHandler.HasFrame(); frameHandler.PrevFrame()) { + auto method = frameHandler.GetMethod(); + pcOffset = FindCatchBlock(method, frameHandler.GetBytecodeOffset()); + if (pcOffset != panda_file::INVALID_OFFSET) { + sp = frameHandler.GetSp(); + constpool = frameHandler.GetConstpool(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + pc = JSMethod::Cast(method)->GetBytecodeArray() + pcOffset; + break; + } + } + if (pcOffset == panda_file::INVALID_OFFSET) { + return; + } + + // set exception to acc + if (exception.IsObjectWrapper()) { + SET_ACC(ObjectWrapper::Cast(exception.GetTaggedObject())->GetValue()); + } else { + SET_ACC(exception); + } + thread->ClearException(); + thread->SetCurrentSPFrame(sp); + DISPATCH_OFFSET(0); + } + HANDLE_OPCODE(OVERFLOW_HANDLER) { + LOG(FATAL, INTERPRETER) << "opcode overflow"; + } +#include "templates/debugger_instruction_handler.inl" +} + +uint8_t EcmaInterpreter::ReadU8(const uint8_t *pc, uint32_t offset) +{ + constexpr uint8_t BIT_8 = 8; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return *reinterpret_cast(pc + offset * BIT_8); +} + +void EcmaInterpreter::InitStackFrame(JSThread *thread) +{ + uint64_t *prevSp = thread->GetCurrentSPFrame(); + FrameState *state = GET_FRAME(prevSp); + state->pc = nullptr; + state->sp = nullptr; + state->method = nullptr; + state->acc = JSTaggedValue::Hole(); + state->constpool = nullptr; + state->profileTypeInfo = JSTaggedValue::Undefined(); + state->prev = nullptr; + state->numActualArgs = 0; +} + +uint32_t EcmaInterpreter::FindCatchBlock(JSMethod *caller, uint32_t pc) +{ + auto *pandaFile = caller->GetPandaFile(); + panda_file::MethodDataAccessor mda(*pandaFile, caller->GetFileId()); + panda_file::CodeDataAccessor cda(*pandaFile, mda.GetCodeId().value()); + + uint32_t pcOffset = panda_file::INVALID_OFFSET; + cda.EnumerateTryBlocks([&pcOffset, pc](panda_file::CodeDataAccessor::TryBlock &try_block) { + if ((try_block.GetStartPc() <= pc) && ((try_block.GetStartPc() + try_block.GetLength()) > pc)) { + try_block.EnumerateCatchBlocks([&](panda_file::CodeDataAccessor::CatchBlock &catch_block) { + pcOffset = catch_block.GetHandlerPc(); + return false; + }); + } + return pcOffset == panda_file::INVALID_OFFSET; + }); + return pcOffset; +} + +void EcmaInterpreter::InterpreterFrameCopyArgs(JSTaggedType *newSp, uint32_t numVregs, uint32_t numActualArgs, + uint32_t numDeclaredArgs) +{ + for (size_t i = 0; i < numVregs; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[i] = JSTaggedValue::VALUE_UNDEFINED; + } + for (size_t i = numActualArgs; i < numDeclaredArgs; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp[numVregs + i] = JSTaggedValue::VALUE_UNDEFINED; + } +} + +JSTaggedValue EcmaInterpreter::GetThisFunction(JSTaggedType *sp) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp) - 1; + auto numVregs = state->method->GetNumVregs(); + return GET_VREG_VALUE(numVregs); +} + +size_t EcmaInterpreter::GetJumpSizeAfterCall(const uint8_t *prevPc) +{ + uint8_t op = *prevPc; + size_t jumpSize; + switch (op) { + case (EcmaOpcode::CALLARG0DYN_IMM8_V8): + jumpSize = BytecodeInstruction::Size(BytecodeInstruction::Format::IMM8_V8); + break; + case (EcmaOpcode::CALLARG1DYN_IMM8_V8_V8): + jumpSize = BytecodeInstruction::Size(BytecodeInstruction::Format::IMM8_V8_V8); + break; + case (EcmaOpcode::CALLARGS2DYN_IMM8_V8_V8_V8): + jumpSize = BytecodeInstruction::Size(BytecodeInstruction::Format::IMM8_V8_V8_V8); + break; + case (EcmaOpcode::CALLARGS3DYN_IMM8_V8_V8_V8_V8): + jumpSize = BytecodeInstruction::Size(BytecodeInstruction::Format::IMM8_V8_V8_V8_V8); + break; + case (EcmaOpcode::CALLIRANGEDYN_IMM8_IMM16_V8): + jumpSize = BytecodeInstruction::Size(BytecodeInstruction::Format::IMM16_V16); + break; + case (EcmaOpcode::CALLITHISRANGEDYN_IMM8_IMM16_V8): + jumpSize = BytecodeInstruction::Size(BytecodeInstruction::Format::IMM8_IMM16_V8); + break; + default: + UNREACHABLE(); + } + + return jumpSize; +} + +JSTaggedValue EcmaInterpreter::GetRuntimeProfileTypeInfo(TaggedType *sp) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp) - 1; + return state->profileTypeInfo; +} + +void EcmaInterpreter::UpdateHotnessCounter(JSThread* thread, TaggedType *sp, int32_t offset) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FrameState *state = reinterpret_cast(sp) - 1; + auto method = state->method; + auto hotnessCounter = static_cast(method->GetHotnessCounter()); + + if (offset < 0) { + hotnessCounter += offset; + if (UNLIKELY(hotnessCounter <= 0)) { + if (state->profileTypeInfo == JSTaggedValue::Undefined()) { + auto numVregs = method->GetNumVregs(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto thisFunc = JSTaggedValue(sp[numVregs]); + auto res = SlowRuntimeStub::NotifyInlineCache( + thread, JSFunction::Cast(thisFunc.GetHeapObject()), method); + state->profileTypeInfo = res; + } else { + hotnessCounter = EcmaInterpreter::METHOD_HOTNESS_THRESHOLD; + } + } + } else { + hotnessCounter += offset; + } + method->SetHotnessCounter(static_cast(hotnessCounter)); +} +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_INTERPRETER_INL_H diff --git a/ecmascript/interpreter/interpreter.h b/ecmascript/interpreter/interpreter.h new file mode 100644 index 0000000000000000000000000000000000000000..d3ba2aa5d1c18b543d723d558efb41896b9ba115 --- /dev/null +++ b/ecmascript/interpreter/interpreter.h @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_INTERPRETER_H +#define PANDA_RUNTIME_ECMASCRIPT_INTERPRETER_H + +#include "ecmascript/js_method.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_thread.h" + +namespace panda::ecmascript { +class ConstantPool; +class ECMAObject; +class GeneratorContext; + +// align with 8 +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +struct FrameState { + const uint8_t *pc; + JSTaggedType *sp; + uint64_t *prev; + JSMethod *method; + // aligned with 8 bits + alignas(sizeof(uint64_t)) ConstantPool *constpool; + JSTaggedValue profileTypeInfo; + JSTaggedValue acc; + JSTaggedValue env; + uint64_t numActualArgs; +}; + +// NOLINTNEXTLINE(bugprone-sizeof-expression) +static const uint32_t FRAME_STATE_SIZE = sizeof(FrameState) / sizeof(uint64_t); + +static constexpr uint32_t RESERVED_CALL_ARGCOUNT = 3; +static constexpr uint32_t RESERVED_INDEX_CALL_TARGET = 0; +static constexpr uint32_t RESERVED_INDEX_NEW_TARGET = 1; +static constexpr uint32_t RESERVED_INDEX_THIS = 2; + +struct CallParams { + ECMAObject *callTarget; + TaggedType newTarget; + TaggedType thisArg; + const TaggedType *argv; + uint32_t argc; +}; + +class EcmaInterpreter { +public: + static const uint32_t METHOD_HOTNESS_THRESHOLD = 100 * 1024; + enum ActualNumArgsOfCall : uint8_t { CALLARG0 = 3, CALLARG1, CALLARGS2, CALLARGS3 }; + + static inline JSTaggedValue Execute(JSThread *thread, const CallParams& params); + static inline JSTaggedValue ExecuteNative(JSThread *thread, const CallParams& params); + static inline JSTaggedValue GeneratorReEnterInterpreter(JSThread *thread, JSHandle context); + static inline void ChangeGenContext(JSThread *thread, JSHandle context); + static inline void ResumeContext(JSThread *thread); + static inline void RunInternal(JSThread *thread, ConstantPool *constpool, const uint8_t *pc, JSTaggedType *sp); + static inline uint8_t ReadU8(const uint8_t *pc, uint32_t offset); + static inline void InitStackFrame(JSThread *thread); + static inline uint32_t FindCatchBlock(JSMethod *caller, uint32_t pc); + static inline size_t GetJumpSizeAfterCall(const uint8_t *prevPc); + + static inline JSTaggedValue GetRuntimeProfileTypeInfo(TaggedType *sp); + static inline void UpdateHotnessCounter(JSThread* thread, TaggedType *sp, int32_t offset); + static inline void InterpreterFrameCopyArgs(JSTaggedType *newSp, uint32_t numVregs, uint32_t numActualArgs, + uint32_t numDeclaredArgs); + static inline void NotifyBytecodePcChanged(JSThread *thread); + static inline JSTaggedValue GetThisFunction(JSTaggedType *sp); +}; + +enum EcmaOpcode { + MOV_DYN_V8_V8, + MOV_DYN_V16_V16, + LDA_STR_ID32, + LDAI_DYN_IMM32, + FLDAI_DYN_IMM64, + LDNAN_IMM8, + LDINFINITY_IMM8, + LDGLOBALTHIS_IMM8, + LDUNDEFINED_IMM8, + LDNULL_IMM8, + LDSYMBOL_IMM8, + LDGLOBAL_IMM8, + LDTRUE_IMM8, + LDFALSE_IMM8, + THROWDYN_IMM8, + TYPEOFDYN_IMM8, + LDLEXENVDYN_IMM8, + POPLEXENVDYN_IMM8, + GETUNMAPPEDARGS_IMM8, + TOBOOLEAN_IMM8, + GETPROPITERATOR_IMM8, + ASYNCFUNCTIONENTER_IMM8, + LDHOLE_IMM8, + RETURNUNDEFINED_IMM8, + CREATEEMPTYOBJECT_IMM8, + CREATEEMPTYARRAY_IMM8, + GETITERATOR_IMM8, + THROWTHROWNOTEXISTS_IMM8, + THROWPATTERNNONCOERCIBLE_IMM8, + LDHOMEOBJECT_IMM8, + THROWDELETESUPERPROPERTY_IMM8, + DEBUGGER_IMM8, + JMP_IMM8, + JMP_IMM16, + JMP_IMM32, + JEQZ_IMM8, + JEQZ_IMM16, + LDA_DYN_V8, + STA_DYN_V8, + LDBOOLEAN_IMM8_V8, + LDNUMBER_IMM8_V8, + LDSTRING_IMM8_V8, + LDBIGINT_IMM8_V8, + ADD2DYN_IMM8_V8, + SUB2DYN_IMM8_V8, + MUL2DYN_IMM8_V8, + DIV2DYN_IMM8_V8, + MOD2DYN_IMM8_V8, + EQDYN_IMM8_V8, + NOTEQDYN_IMM8_V8, + LESSDYN_IMM8_V8, + LESSEQDYN_IMM8_V8, + GREATERDYN_IMM8_V8, + GREATEREQDYN_IMM8_V8, + SHL2DYN_IMM8_V8, + SHR2DYN_IMM8_V8, + ASHR2DYN_IMM8_V8, + AND2DYN_IMM8_V8, + OR2DYN_IMM8_V8, + XOR2DYN_IMM8_V8, + TONUMBER_IMM8_V8, + NEGDYN_IMM8_V8, + NOTDYN_IMM8_V8, + INCDYN_IMM8_V8, + DECDYN_IMM8_V8, + EXPDYN_IMM8_V8, + ISINDYN_IMM8_V8, + INSTANCEOFDYN_IMM8_V8, + STRICTNOTEQDYN_IMM8_V8, + STRICTEQDYN_IMM8_V8, + RESUMEGENERATOR_IMM8_V8, + GETRESUMEMODE_IMM8_V8, + CREATEGENERATOROBJ_IMM8_V8, + THROWUNDEFINED_IMM8_V8, + THROWCONSTASSIGNMENT_IMM8_V8, + GETTEMPLATEOBJECT_IMM8_V8, + GETNEXTPROPNAME_IMM8_V8, + CALLARG0DYN_IMM8_V8, + THROWIFNOTOBJECT_IMM8_V8, + ITERNEXT_IMM8_V8, + CLOSEITERATOR_IMM8_V8, + COPYMODULE_IMM8_V8, + SUPERCALLSPREAD_IMM8_V8, + LDOBJECT_IMM8_V8_V8, + LDFUNCTION_IMM8_V8_V8, + DELOBJPROP_IMM8_V8_V8, + DEFINEGLOBALVAR_IMM8_V8_V8, + DEFINELOCALVAR_IMM8_V8_V8, + DEFINEFUNCEXPR_IMM8_V8_V8, + REFEQDYN_IMM8_V8_V8, + CALLRUNTIMERANGE_IMM8_V8_V8, + NEWOBJSPREADDYN_IMM8_V8_V8, + CREATEITERRESULTOBJ_IMM8_V8_V8, + SUSPENDGENERATOR_IMM8_V8_V8, + ASYNCFUNCTIONAWAITUNCAUGHT_IMM8_V8_V8, + THROWUNDEFINEDIFHOLE_IMM8_V8_V8, + CALLARG1DYN_IMM8_V8_V8, + COPYDATAPROPERTIES_IMM8_V8_V8, + STARRAYSPREAD_IMM8_V8_V8, + GETITERATORNEXT_IMM8_V8_V8, + SETOBJECTWITHPROTO_IMM8_V8_V8, + CALLSPREADDYN_IMM8_V8_V8_V8, + ASYNCFUNCTIONRESOLVE_IMM8_V8_V8_V8, + ASYNCFUNCTIONREJECT_IMM8_V8_V8_V8, + CALLARGS2DYN_IMM8_V8_V8_V8, + CALLARGS3DYN_IMM8_V8_V8_V8_V8, + DEFINEGETTERSETTERBYVALUE_IMM8_V8_V8_V8_V8, + TRYLDGLOBALBYVALUE_IMM8_IMM16_V8, + NEWOBJDYNRANGE_IMM8_IMM16_V8, + TRYSTGLOBALBYVALUE_IMM8_IMM16_V8, + CALLIRANGEDYN_IMM8_IMM16_V8, + CALLITHISRANGEDYN_IMM8_IMM16_V8, + SUPERCALL_IMM8_IMM16_V8, + LDOBJBYVALUE_IMM8_IMM16_V8_V8, + STOBJBYVALUE_IMM8_IMM16_V8_V8, + LDOBJBYINDEX_IMM8_IMM16_V8_V8, + STOBJBYINDEX_IMM8_IMM16_V8_V8, + STOWNBYINDEX_IMM8_IMM16_V8_V8, + STOWNBYVALUE_IMM8_IMM16_V8_V8, + CREATEOBJECTWITHEXCLUDEDKEYS_IMM8_IMM16_V8_V8, + STSUPERBYVALUE_IMM8_IMM16_V8_V8, + LDSUPERBYVALUE_IMM8_IMM16_V8_V8, + IMPORTMODULE_IMM8_ID32, + STMODULEVAR_IMM8_ID32, + DEFINEFUNCDYN_IMM8_ID16_V8, + DEFINENCFUNCDYN_IMM8_ID16_V8, + DEFINEGENERATORFUNC_IMM8_ID16_V8, + DEFINEASYNCFUNC_IMM8_ID16_V8, + DEFINEMETHOD_IMM8_ID16_V8, + TRYLDGLOBALBYNAME_IMM8_ID32_IMM16, + TRYSTGLOBALBYNAME_IMM8_ID32_IMM16, + LDGLOBALVAR_IMM8_ID32_IMM16, + STGLOBALVAR_IMM8_ID32_IMM16, + LDOBJBYNAME_IMM8_ID32_IMM16_V8, + STOBJBYNAME_IMM8_ID32_IMM16_V8, + STOWNBYNAME_IMM8_ID32_IMM16_V8, + LDMODVARBYNAME_IMM8_ID32_IMM16_V8, + LDSUPERBYNAME_IMM8_ID32_IMM16_V8, + STSUPERBYNAME_IMM8_ID32_IMM16_V8, + STLEXVARDYN_IMM8_IMM16_IMM16_V8, + LDLEXVARDYN_IMM8_IMM16_IMM16, + NEWLEXENVDYN_IMM8_IMM16, + COPYRESTARGS_IMM8_IMM16, + CREATEOBJECTWITHBUFFER_IMM8_IMM16, + CREATEARRAYWITHBUFFER_IMM8_IMM16, + CREATEOBJECTHAVINGMETHOD_IMM8_IMM16, + THROWIFSUPERNOTCORRECTCALL_IMM8_IMM16, + DEFINECLASSWITHBUFFER_IMM8_ID16_IMM16_V8_V8, + RETURN_DYN, + MOV_V4_V4, + JNEZ_IMM8, + JNEZ_IMM16, + LAST_OPCODE, +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_INTERPRETER_H diff --git a/ecmascript/interpreter/slow_runtime_helper.cpp b/ecmascript/interpreter/slow_runtime_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c70d6b694ca67d15a62264b17904c09276ad7319 --- /dev/null +++ b/ecmascript/interpreter/slow_runtime_helper.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "slow_runtime_helper.h" +#include "ecmascript/global_env.h" +#include "ecmascript/interpreter/frame_handler.h" +#include "ecmascript/interpreter/interpreter-inl.h" +#include "ecmascript/js_generator_object.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript { +JSTaggedValue SlowRuntimeHelper::CallBoundFunction(JSThread *thread, JSHandle boundFunc, + JSHandle obj, JSHandle args) +{ + uint32_t numArgsReal = args->GetLength(); + + JSHandle argsBound(thread, boundFunc->GetBoundArguments()); + uint32_t numArgsBound = argsBound->GetLength(); + + JSHandle targetFunc(thread, boundFunc->GetBoundTarget()); + if (targetFunc->IsClassConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "class constructor cannot called without 'new'", + JSTaggedValue::Exception()); + } + + uint32_t arraySize = numArgsBound + numArgsReal; + JSHandle argsNew = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(arraySize); + + uint32_t idx = 0; + for (uint32_t i = 0; i < numArgsBound; i++) { + argsNew->Set(thread, idx++, argsBound->Get(i)); + } + for (uint32_t i = 0; i < numArgsReal; i++) { + argsNew->Set(thread, idx++, args->Get(i)); + } + + JSHandle newTarget(thread, JSTaggedValue::Undefined()); + return InvokeJsFunction(thread, targetFunc, obj, newTarget, argsNew); +} + +JSTaggedValue SlowRuntimeHelper::NewObject(JSThread *thread, JSHandle func, + JSHandle newTarget, JSHandle args) +{ + if (!func->IsHeapObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "function is nullptr", JSTaggedValue::Exception()); + } + + if (!func->IsJSFunction()) { + if (func->IsBoundFunction()) { + JSTaggedValue result = + JSBoundFunction::ConstructInternal(thread, JSHandle::Cast(func), args, newTarget); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result; + } + + if (func->IsJSProxy()) { + JSTaggedValue jsObj = JSProxy::ConstructInternal(thread, JSHandle(func), args, newTarget); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return jsObj; + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructed NonConstructable", JSTaggedValue::Exception()); + } + + JSHandle jsFunc = JSHandle::Cast(func); + ASSERT(jsFunc->GetCallTarget() != nullptr); + ASSERT(JSFunction::Cast(newTarget->GetTaggedObject())->GetCallTarget() != nullptr); + + if (jsFunc->GetCallTarget()->IsNative()) { + if (jsFunc->IsBuiltinsConstructor()) { + return InvokeJsFunction(thread, jsFunc, JSHandle(thread, JSTaggedValue::Undefined()), + newTarget, args); + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructed NonConstructable", JSTaggedValue::Exception()); + } + + JSTaggedValue result = JSFunction::ConstructInternal(thread, jsFunc, args, newTarget); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result; +} + +void SlowRuntimeHelper::SaveFrameToContext(JSThread *thread, JSHandle context) +{ + EcmaFrameHandler frameHandler(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + uint32_t nregs = frameHandler.GetSize(); + JSHandle regsArray = factory->NewTaggedArray(nregs); + for (uint32_t i = 0; i < nregs; i++) { + JSTaggedValue value = frameHandler.GetVRegValue(i); + regsArray->Set(thread, i, value); + } + context->SetRegsArray(thread, regsArray.GetTaggedValue()); + context->SetMethod(thread, frameHandler.GetFunction()); + + context->SetAcc(thread, frameHandler.GetAcc()); + context->SetNRegs(thread, JSTaggedValue(nregs)); + context->SetBCOffset(thread, JSTaggedValue(frameHandler.GetBytecodeOffset())); + context->SetLexicalEnv(thread, thread->GetCurrentLexenv()); +} + +JSTaggedValue ConstructGeneric(JSThread *thread, JSHandle ctor, JSHandle newTgt, + JSHandle preArgs, uint32_t argsCount, uint32_t baseArgLocation) +{ + if (!ctor->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception()); + } + + JSHandle obj(thread, JSTaggedValue::Undefined()); + if (!ctor->IsBuiltinsConstructor() && ctor->IsBase()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + obj = JSHandle(factory->NewJSObjectByConstructor(ctor, newTgt)); + } + uint32_t preArgsSize = preArgs->IsUndefined() ? 0 : JSHandle::Cast(preArgs)->GetLength(); + const array_size_t size = preArgsSize + argsCount; + CVector values; + values.reserve(size); + + JSMethod *method = ctor->GetCallTarget(); + if (method == nullptr) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Undefined target", JSTaggedValue::Exception()); + } + + // Add the input parameter + EcmaFrameHandler frameHandler(thread); + CallParams params; + params.callTarget = ECMAObject::Cast(*ctor); + params.newTarget = newTgt.GetTaggedType(); + params.thisArg = obj.GetTaggedType(); + params.argc = size; + // add preArgs when boundfunction is encountered + if (preArgsSize > 0) { + JSHandle tgaPreArgs = JSHandle::Cast(preArgs); + for (array_size_t i = 0; i < preArgsSize; ++i) { + JSTaggedValue value = tgaPreArgs->Get(i); + values.emplace_back(value.GetRawData()); + } + for (array_size_t i = 0; i < argsCount; ++i) { + JSTaggedValue value = frameHandler.GetVRegValue(baseArgLocation + i); + values.emplace_back(value.GetRawData()); + } + params.argv = values.data(); + } else { + for (array_size_t i = 0; i < argsCount; ++i) { + JSTaggedValue value = frameHandler.GetVRegValue(baseArgLocation + i); + values.emplace_back(value.GetRawData()); + } + params.argv = values.data(); + } + + JSTaggedValue resultValue = EcmaInterpreter::Execute(thread, params); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 9.3.2 [[Construct]] (argumentsList, newTarget) + if (ctor->IsBuiltinsConstructor() || resultValue.IsECMAObject()) { + return resultValue; + } + + if (ctor->IsBase()) { + return obj.GetTaggedValue(); + } + if (!resultValue.IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "function is non-constructor", JSTaggedValue::Exception()); + } + return obj.GetTaggedValue(); +} + +JSTaggedValue ConstructBoundFunction(JSThread *thread, JSHandle ctor, JSHandle newTgt, + JSHandle preArgs, uint32_t argsCount, uint32_t baseArgLocation) +{ + JSHandle target(thread, ctor->GetBoundTarget()); + ASSERT(target->IsConstructor()); + + JSHandle boundArgs(thread, ctor->GetBoundArguments()); + JSMutableHandle newPreArgs(thread, preArgs.GetTaggedValue()); + if (newPreArgs->IsUndefined()) { + newPreArgs.Update(boundArgs.GetTaggedValue()); + } else { + newPreArgs.Update( + TaggedArray::Append(thread, boundArgs, JSHandle::Cast(preArgs)).GetTaggedValue()); + } + JSMutableHandle newTargetMutable(thread, newTgt.GetTaggedValue()); + if (JSTaggedValue::SameValue(ctor.GetTaggedValue(), newTgt.GetTaggedValue())) { + newTargetMutable.Update(target.GetTaggedValue()); + } + return SlowRuntimeHelper::Construct(thread, target, newTargetMutable, newPreArgs, argsCount, baseArgLocation); +} + +JSTaggedValue ConstructProxy(JSThread *thread, JSHandle ctor, JSHandle newTgt, + JSHandle preArgs, uint32_t argsCount, uint32_t baseArgLocation) +{ + // step 1 ~ 4 get ProxyHandler and ProxyTarget + JSHandle handler(thread, ctor->GetHandler()); + if (handler->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor: handler is null", JSTaggedValue::Exception()); + } + ASSERT(handler->IsJSObject()); + JSHandle target(thread, ctor->GetTarget()); + + // 5.Let trap be GetMethod(handler, "construct"). + JSHandle key(thread->GlobalConstants()->GetHandledProxyConstructString()); + JSHandle method = JSObject::GetMethod(thread, handler, key); + + // 6.ReturnIfAbrupt(trap). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7.If trap is undefined, then + // a.Assert: target has a [[Construct]] internal method. + // b.Return Construct(target, argumentsList, newTarget). + if (method->IsUndefined()) { + ASSERT(target->IsConstructor()); + return SlowRuntimeHelper::Construct(thread, target, newTgt, preArgs, argsCount, baseArgLocation); + } + + // 8.Let argArray be CreateArrayFromList(argumentsList). + uint32_t preArgsSize = preArgs->IsUndefined() ? 0 : JSHandle::Cast(preArgs)->GetLength(); + const array_size_t size = preArgsSize + argsCount; + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(size); + JSHandle tgaPreArgs = JSHandle::Cast(preArgs); + if (preArgsSize > 0) { + for (array_size_t i = 0; i < preArgsSize; ++i) { + JSTaggedValue value = tgaPreArgs->Get(i); + args->Set(thread, i, value); + } + } + EcmaFrameHandler frameHandler(thread); + for (array_size_t i = 0; i < argsCount; ++i) { + JSTaggedValue value = frameHandler.GetVRegValue(baseArgLocation + i); + args->Set(thread, i + preArgsSize, value); + } + + // step 8 ~ 9 Call(trap, handler, «target, argArray, newTarget »). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle nextArgv(factory->NewTaggedArray(3)); // 3: «target, argArray, newTarget » + nextArgv->Set(thread, 0, target.GetTaggedValue()); + nextArgv->Set(thread, 1, args.GetTaggedValue()); + nextArgv->Set(thread, 2, newTgt.GetTaggedValue()); // 2: the third arg is new target + JSTaggedValue newObjValue = JSFunction::Call(thread, method, handler, nextArgv); + // 10.ReturnIfAbrupt(newObj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11.If Type(newObj) is not Object, throw a TypeError exception. + if (!newObjValue.IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new object is not object", JSTaggedValue::Exception()); + } + // 12.Return newObj. + return newObjValue; +} + +JSTaggedValue SlowRuntimeHelper::Construct(JSThread *thread, JSHandle ctor, + JSHandle newTarget, JSHandle preArgs, + uint32_t argsCount, uint32_t baseArgLocation) +{ + if (newTarget->IsUndefined()) { + newTarget = ctor; + } + + if (!(newTarget->IsConstructor() && ctor->IsConstructor())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception()); + } + if (ctor->IsJSFunction()) { + return ConstructGeneric(thread, JSHandle::Cast(ctor), newTarget, preArgs, argsCount, + baseArgLocation); + } + if (ctor->IsBoundFunction()) { + return ConstructBoundFunction(thread, JSHandle::Cast(ctor), newTarget, preArgs, argsCount, + baseArgLocation); + } + if (ctor->IsJSProxy()) { + return ConstructProxy(thread, JSHandle::Cast(ctor), newTarget, preArgs, argsCount, baseArgLocation); + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor NonConstructor", JSTaggedValue::Exception()); +} +} // namespace panda::ecmascript diff --git a/ecmascript/interpreter/slow_runtime_helper.h b/ecmascript/interpreter/slow_runtime_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..c82a6da8d90c12982c1b3a22d6f3a31809552fe7 --- /dev/null +++ b/ecmascript/interpreter/slow_runtime_helper.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_SLOW_RUNTIME_HELPER_H +#define PANDA_RUNTIME_ECMASCRIPT_SLOW_RUNTIME_HELPER_H + +#include "ecmascript/js_function.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +class SlowRuntimeHelper { +public: + static JSTaggedValue NewObject(JSThread *thread, JSHandle func, JSHandle newTarget, + JSHandle args); + + static JSTaggedValue CallBoundFunction(JSThread *thread, JSHandle boundFunc, + JSHandle obj, JSHandle args); + + static void SaveFrameToContext(JSThread *thread, JSHandle context); + + static JSTaggedValue Construct(JSThread *thread, JSHandle ctor, JSHandle newTarget, + JSHandle preArgs, uint32_t argsCount, uint32_t baseArgLocation); +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_SLOW_RUNTIME_HELPER_H diff --git a/ecmascript/interpreter/slow_runtime_stub.cpp b/ecmascript/interpreter/slow_runtime_stub.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8e5c86d631c12beda922fb63408732cd1f3e85ab --- /dev/null +++ b/ecmascript/interpreter/slow_runtime_stub.cpp @@ -0,0 +1,1715 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/interpreter/slow_runtime_stub.h" + +#include "ecmascript/base/number_helper.h" + +#include "ecmascript/class_linker/program_object-inl.h" +#include "ecmascript/ecma_module.h" +#include "ecmascript/global_dictionary-inl.h" +#include "ecmascript/ic/profile_type_info.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/interpreter/slow_runtime_helper.h" +#include "ecmascript/js_arguments.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_async_function.h" +#include "ecmascript/js_for_in_iterator.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_generator_object.h" +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_proxy.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/runtime_call_id.h" +#include "ecmascript/template_string.h" +#include "ecmascript/vmstat/runtime_stat.h" + +namespace panda::ecmascript { +JSTaggedValue SlowRuntimeStub::CallSpreadDyn(JSThread *thread, JSTaggedValue func, JSTaggedValue obj, + JSTaggedValue array) +{ + INTERPRETER_TRACE(thread, CallSpreadDyn); + if ((!obj.IsUndefined() && !obj.IsECMAObject()) || !func.IsJSFunction() || !array.IsJSArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "cannot Callspread", JSTaggedValue::Exception()); + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle jsFunc(thread, func); + JSHandle jsArray(thread, array); + JSHandle taggedObj(thread, obj); + + JSHandle coretypesArray(thread, GetCallSpreadArgs(thread, jsArray.GetTaggedValue())); + + JSHandle newTarget(thread, JSTaggedValue::Undefined()); + JSTaggedValue res = InvokeJsFunction(thread, jsFunc, taggedObj, newTarget, coretypesArray); + + return res; +} + +JSTaggedValue SlowRuntimeStub::NegDyn(JSThread *thread, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle input(thread, value); + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, input); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (number.IsInt()) { + int32_t intValue = number.GetInt(); + if (intValue == 0) { + return JSTaggedValue(-0.0); + } + return JSTaggedValue(-intValue); + } + if (number.IsDouble()) { + return JSTaggedValue(-number.GetDouble()); + } + + UNREACHABLE(); +} + +JSTaggedValue SlowRuntimeStub::AsyncFunctionEnter(JSThread *thread) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. create promise + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle promiseFunc = globalEnv->GetPromiseFunction(); + + JSHandle promiseObject = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(promiseFunc), promiseFunc)); + promiseObject->SetPromiseState(thread, JSTaggedValue(static_cast(PromiseStatus::PENDING))); + // 2. create asyncfuncobj + JSHandle asyncFuncObj = factory->NewJSAsyncFuncObject(); + asyncFuncObj->SetPromise(thread, promiseObject); + + JSHandle context = factory->NewGeneratorContext(); + context->SetGeneratorObject(thread, asyncFuncObj); + + // change state to EXECUTING + asyncFuncObj->SetGeneratorState(thread, JSTaggedValue(static_cast(JSGeneratorState::EXECUTING))); + asyncFuncObj->SetGeneratorContext(thread, context); + + // 3. return asyncfuncobj + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return asyncFuncObj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::ToNumber(JSThread *thread, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, Tonumber); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle number(thread, value); + // may return exception + return JSTaggedValue::ToNumber(thread, number); +} + +JSTaggedValue SlowRuntimeStub::NotDyn(JSThread *thread, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle valueHandle(thread, value); + int32_t number = JSTaggedValue::ToInt32(thread, valueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(~number); // NOLINT(hicpp-signed-bitwise) +} + +JSTaggedValue SlowRuntimeStub::IncDyn(JSThread *thread, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle valueHandle(thread, value); + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, valueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (++number); +} + +JSTaggedValue SlowRuntimeStub::DecDyn(JSThread *thread, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle valueHandle(thread, value); + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, valueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (--number); +} + +void SlowRuntimeStub::ThrowDyn(JSThread *thread, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj(thread, value); + JSHandle wrapperObject = factory->NewObjectWrapper(obj); + thread->SetException(wrapperObject.GetTaggedValue()); +} + +JSTaggedValue SlowRuntimeStub::GetPropIterator(JSThread *thread, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, value); + JSHandle iteratorHandle = JSObject::EnumerateObjectProperties(thread, objHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return iteratorHandle.GetTaggedValue(); +} + +void SlowRuntimeStub::ThrowConstAssignment(JSThread *thread, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle name(thread, value.GetTaggedObject()); + JSHandle info = factory->NewFromString("Assignment to const variable "); + + JSHandle msg = factory->ConcatFromString(info, name); + THROW_NEW_ERROR_AND_RETURN(thread, factory->NewJSError(base::ErrorType::TYPE_ERROR, msg).GetTaggedValue()); +} + +JSTaggedValue SlowRuntimeStub::Add2Dyn(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, Add2Dyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + if (leftValue->IsString() && rightValue->IsString()) { + JSHandle stringA0 = JSTaggedValue::ToString(thread, leftValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle stringA1 = JSTaggedValue::ToString(thread, rightValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + EcmaString *newString = EcmaString::Concat(stringA0, stringA1, ecma_vm); + return JSTaggedValue(newString); + } + JSHandle primitiveA0(thread, JSTaggedValue::ToPrimitive(thread, leftValue)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle primitiveA1(thread, JSTaggedValue::ToPrimitive(thread, rightValue)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // contain string + if (primitiveA0->IsString() || primitiveA1->IsString()) { + JSHandle stringA0 = JSTaggedValue::ToString(thread, primitiveA0); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle stringA1 = JSTaggedValue::ToString(thread, primitiveA1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + EcmaString *newString = EcmaString::Concat(stringA0, stringA1, ecma_vm); + return JSTaggedValue(newString); + } + JSTaggedNumber taggedValueA0 = JSTaggedValue::ToNumber(thread, primitiveA0); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedNumber taggedValueA1 = JSTaggedValue::ToNumber(thread, primitiveA1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double a0Double = taggedValueA0.GetNumber(); + double a1Double = taggedValueA1.GetNumber(); + return JSTaggedValue(a0Double + a1Double); +} + +JSTaggedValue SlowRuntimeStub::Sub2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, Sub2Dyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + + JSTaggedNumber number0 = JSTaggedValue::ToNumber(thread, leftHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedNumber number1 = JSTaggedValue::ToNumber(thread, rightHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return number0 - number1; +} + +JSTaggedValue SlowRuntimeStub::Mul2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, Mul2Dyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + + // 6. Let lnum be ToNumber(leftValue). + JSTaggedNumber primitiveA = JSTaggedValue::ToNumber(thread, leftValue); + // 7. ReturnIfAbrupt(lnum). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 8. Let rnum be ToNumber(rightValue). + JSTaggedNumber primitiveB = JSTaggedValue::ToNumber(thread, rightValue); + // 9. ReturnIfAbrupt(rnum). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 12.6.3.1 Applying the * Operator + return primitiveA * primitiveB; +} + +JSTaggedValue SlowRuntimeStub::Div2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, Div2Dyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSTaggedNumber leftNumber = JSTaggedValue::ToNumber(thread, JSHandle(thread, left)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double dLeft = leftNumber.GetNumber(); + JSTaggedNumber rightNumber = JSTaggedValue::ToNumber(thread, JSHandle(thread, right)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double dRight = rightNumber.GetNumber(); + if (dRight == 0) { + if (dLeft == 0 || std::isnan(dLeft)) { + return JSTaggedValue(base::NAN_VALUE); + } + bool positive = ((bit_cast(dRight) & base::DOUBLE_SIGN_MASK) == + (bit_cast(dLeft) & base::DOUBLE_SIGN_MASK)); + return JSTaggedValue(positive ? base::POSITIVE_INFINITY : -base::POSITIVE_INFINITY); + } + return JSTaggedValue(dLeft / dRight); +} + +JSTaggedValue SlowRuntimeStub::Mod2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, Mod2Dyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSTaggedNumber leftNumber = JSTaggedValue::ToNumber(thread, JSHandle(thread, left)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double dLeft = leftNumber.GetNumber(); + JSTaggedNumber rightNumber = JSTaggedValue::ToNumber(thread, JSHandle(thread, right)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double dRight = rightNumber.GetNumber(); + // 12.6.3.3 Applying the % Operator + if ((dRight == 0.0) || std::isnan(dRight) || std::isnan(dLeft) || std::isinf(dLeft)) { + return JSTaggedValue(base::NAN_VALUE); + } + if ((dLeft == 0.0) || std::isinf(dRight)) { + return JSTaggedValue(dLeft); + } + + return JSTaggedValue(std::fmod(dLeft, dRight)); +} + +JSTaggedValue SlowRuntimeStub::EqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, EqDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + bool ret = JSTaggedValue::Equal(thread, leftValue, rightValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue SlowRuntimeStub::NotEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, NotEqDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + bool ret = JSTaggedValue::Equal(thread, leftValue, rightValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::False() : JSTaggedValue::True()); +} + +JSTaggedValue SlowRuntimeStub::LessDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, LessDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + bool ret = JSTaggedValue::Compare(thread, leftValue, rightValue) == ComparisonResult::LESS; + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue SlowRuntimeStub::LessEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, LessEqDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + bool ret = JSTaggedValue::Compare(thread, leftValue, rightValue) <= ComparisonResult::EQUAL; + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue SlowRuntimeStub::GreaterDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, GreaterDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + bool ret = JSTaggedValue::Compare(thread, leftValue, rightValue) == ComparisonResult::GREAT; + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue SlowRuntimeStub::GreaterEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, GreaterEqDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + ComparisonResult comparison = JSTaggedValue::Compare(thread, leftValue, rightValue); + bool ret = (comparison == ComparisonResult::GREAT) || (comparison == ComparisonResult::EQUAL); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue SlowRuntimeStub::ToJSTaggedValueWithInt32(JSThread *thread, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle valueHandle(thread, value); + int32_t res = JSTaggedValue::ToInt32(thread, valueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(res); +} + +JSTaggedValue SlowRuntimeStub::ToJSTaggedValueWithUint32(JSThread *thread, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle valueHandle(thread, value); + int32_t res = JSTaggedValue::ToUint32(thread, valueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(res); +} + +JSTaggedValue SlowRuntimeStub::DelObjProp(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop) +{ + INTERPRETER_TRACE(thread, Delobjprop); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, prop); + JSHandle jsObj(JSTaggedValue::ToObject(thread, objHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, propHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool ret = JSTaggedValue::DeletePropertyOrThrow(thread, jsObj, propKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(ret); +} + +JSTaggedValue SlowRuntimeStub::NewObjDynRange(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, + uint16_t firstArgIdx, uint16_t length) +{ + INTERPRETER_TRACE(thread, NewobjDynrange); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle funcHandle(thread, func); + JSHandle newTargetHandle(thread, newTarget); + + JSHandle preArgs(thread, JSTaggedValue::Undefined()); + auto tagged = SlowRuntimeHelper::Construct(thread, funcHandle, newTargetHandle, preArgs, length, firstArgIdx); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return tagged; +} + +JSTaggedValue SlowRuntimeStub::CreateObjectWithExcludedKeys(JSThread *thread, uint16_t numKeys, JSTaggedValue objVal, + uint16_t firstArgRegIdx) +{ + INTERPRETER_TRACE(thread, CreateObjectWithExcludedKeys); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + ASSERT(objVal.IsJSObject()); + JSHandle obj(thread, objVal); + array_size_t numExcludedKeys = 0; + JSHandle excludedKeys = factory->NewTaggedArray(numKeys + 1); + EcmaFrameHandler frameHandler(thread); + JSTaggedValue excludedKey = frameHandler.GetVRegValue(firstArgRegIdx); + if (!excludedKey.IsUndefined()) { + numExcludedKeys = numKeys + 1; + excludedKeys->Set(thread, 0, excludedKey); + for (array_size_t i = 1; i < numExcludedKeys; i++) { + excludedKey = frameHandler.GetVRegValue(firstArgRegIdx + i); + excludedKeys->Set(thread, i, excludedKey); + } + } + + uint32_t numAllKeys = obj->GetNumberOfKeys(); + JSHandle allKeys = factory->NewTaggedArray(numAllKeys); + JSObject::GetAllKeys(thread, obj, 0, allKeys); + + JSHandle restObj = factory->NewEmptyJSObject(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < numAllKeys; i++) { + key.Update(allKeys->Get(i)); + bool isExcludedKey = false; + for (uint32_t j = 0; j < numExcludedKeys; j++) { + if (JSTaggedValue::Equal(thread, key, JSHandle(thread, excludedKeys->Get(j)))) { + isExcludedKey = true; + break; + } + } + if (!isExcludedKey) { + PropertyDescriptor desc(thread); + bool success = JSObject::GetOwnProperty(thread, obj, key, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (success && desc.IsEnumerable()) { + JSHandle value = JSObject::GetProperty(thread, obj, key).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::SetProperty(thread, restObj, key, value, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + } + return restObj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::ExpDyn(JSThread *thread, JSTaggedValue base, JSTaggedValue exponent) +{ + INTERPRETER_TRACE(thread, ExpDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSTaggedNumber baseNumber = JSTaggedValue::ToNumber(thread, JSHandle(thread, base)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double doubleBase = baseNumber.GetNumber(); + JSTaggedNumber exponentNumber = JSTaggedValue::ToNumber(thread, JSHandle(thread, exponent)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double doubleExponent = exponentNumber.GetNumber(); + if (std::abs(doubleBase) == 1 && std::isinf(doubleExponent)) { + return JSTaggedValue(base::NAN_VALUE); + } + + if ((doubleBase == 0 && (bit_cast(doubleBase) & base::DOUBLE_SIGN_MASK) == base::DOUBLE_SIGN_MASK) && + std::isfinite(doubleExponent) && base::NumberHelper::TruncateDouble(doubleExponent) == doubleExponent && + base::NumberHelper::TruncateDouble(doubleExponent / 2) + base::HALF == (doubleExponent / 2)) { // 2: half + if (doubleExponent > 0) { + return JSTaggedValue(-0.0); + } + if (doubleExponent < 0) { + return JSTaggedValue(-base::POSITIVE_INFINITY); + } + } + return JSTaggedValue(std::pow(doubleBase, doubleExponent)); +} + +JSTaggedValue SlowRuntimeStub::IsInDyn(JSThread *thread, JSTaggedValue prop, JSTaggedValue obj) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle propHandle(thread, prop); + JSHandle objHandle(thread, obj); + if (!objHandle->IsECMAObject()) { + return ThrowTypeError(thread, "Cannot use 'in' operator in Non-Object"); + } + JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, propHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool ret = JSTaggedValue::HasProperty(thread, objHandle, propKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(ret); +} + +JSTaggedValue SlowRuntimeStub::InstanceofDyn(JSThread *thread, JSTaggedValue obj, JSTaggedValue target) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSHandle targetHandle(thread, target); + bool ret = JSObject::InstanceOf(thread, objHandle, targetHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(ret); +} + +JSTaggedValue SlowRuntimeStub::NewLexicalEnvDyn(JSThread *thread, uint16_t numVars) +{ + INTERPRETER_TRACE(thread, NewlexenvDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newEnv = factory->NewLexicalEnv(numVars); + + JSTaggedValue currentLexenv = thread->GetCurrentLexenv(); + newEnv->SetParentEnv(thread, currentLexenv); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return newEnv.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CreateIterResultObj(JSThread *thread, JSTaggedValue value, JSTaggedValue flag) +{ + INTERPRETER_TRACE(thread, CreateIterResultObj); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle valueHandle(thread, value); + ASSERT(flag.IsBoolean()); + bool done = flag.IsTrue(); + JSHandle iter = JSIterator::CreateIterResultObject(thread, valueHandle, done); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return iter.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CreateGeneratorObj(JSThread *thread, JSTaggedValue genFunc) +{ + INTERPRETER_TRACE(thread, CreateGeneratorObj); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle generatorFunction(thread, genFunc); + JSHandle obj = factory->NewJSGeneratorObject(generatorFunction); + JSHandle context = factory->NewGeneratorContext(); + context->SetGeneratorObject(thread, obj.GetTaggedValue()); + + // change state to SUSPENDED_START + obj->SetGeneratorState(thread, JSTaggedValue(static_cast(JSGeneratorState::SUSPENDED_START))); + obj->SetGeneratorContext(thread, context); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return obj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::SuspendGenerator(JSThread *thread, JSTaggedValue genObj, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, SuspendGenerator); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle generatorObjectHandle(thread, genObj); + JSHandle genContextHandle(thread, generatorObjectHandle->GetGeneratorContext()); + JSHandle valueHandle(thread, value); + // save stack, should copy cur_frame, function execute over will free cur_frame + SlowRuntimeHelper::SaveFrameToContext(thread, genContextHandle); + + // change state to SuspendedYield + if (generatorObjectHandle->IsExecuting()) { + generatorObjectHandle->SetGeneratorState(thread, + JSTaggedValue(static_cast(JSGeneratorState::SUSPENDED_YIELD))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return valueHandle.GetTaggedValue(); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return generatorObjectHandle.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::AsyncFunctionAwaitUncaught(JSThread *thread, JSTaggedValue asyncFuncObj, + JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, AsyncFunctionAwaitUncaught); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle asyncFuncObjHandle(thread, asyncFuncObj); + JSHandle valueHandle(thread, value); + JSAsyncFunction::AsyncFunctionAwait(thread, asyncFuncObjHandle, valueHandle); + JSHandle promise(thread, asyncFuncObjHandle->GetPromise()); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return promise.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::AsyncFunctionResolveOrReject(JSThread *thread, JSTaggedValue asyncFuncObj, + JSTaggedValue value, bool is_resolve) +{ + INTERPRETER_TRACE(thread, AsyncFunctionResolveOrReject); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle asyncFuncObjHandle(thread, asyncFuncObj); + JSHandle promise(thread, asyncFuncObjHandle->GetPromise()); + JSHandle valueHandle(thread, value); + + // ActivePromise + JSHandle reactions = JSPromise::CreateResolvingFunctions(thread, promise); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle thisArg = globalConst->GetHandledUndefined(); + JSHandle args = factory->NewTaggedArray(1); + args->Set(thread, 0, value); + JSHandle activeFunc; + if (is_resolve) { + activeFunc = JSHandle(thread, reactions->GetResolveFunction()); + } else { + activeFunc = JSHandle(thread, reactions->GetRejectFunction()); + } + [[maybe_unused]] JSTaggedValue res = JSFunction::Call(thread, activeFunc, thisArg, args); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return promise.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::NewObjSpreadDyn(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, + JSTaggedValue array) +{ + INTERPRETER_TRACE(thread, NewobjspreadDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle funcHandle(thread, func); + JSHandle newTargetHandle(thread, newTarget); + JSHandle jsArray(thread, array); + if (!jsArray->IsJSArray()) { + return ThrowTypeError(thread, "Cannot Newobjspread"); + } + + uint32_t length = JSHandle::Cast(jsArray)->GetArrayLength(); + JSHandle argsArray = factory->NewTaggedArray(length); + for (array_size_t i = 0; i < length; ++i) { + auto prop = JSTaggedValue::GetProperty(thread, jsArray, i).GetValue(); + argsArray->Set(thread, i, prop); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + auto tagged = SlowRuntimeHelper::NewObject(thread, funcHandle, newTargetHandle, argsArray); + return tagged; +} + +void SlowRuntimeStub::ThrowUndefinedIfHole(JSThread *thread, JSTaggedValue obj) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle name(thread, obj); + JSHandle info = factory->NewFromString(" is not initialized"); + + JSHandle msg = factory->ConcatFromString(info, name); + THROW_NEW_ERROR_AND_RETURN(thread, factory->NewJSError(base::ErrorType::REFERENCE_ERROR, msg).GetTaggedValue()); +} + +JSTaggedValue SlowRuntimeStub::ThrowIfSuperNotCorrectCall(JSThread *thread, uint16_t index, JSTaggedValue thisValue) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + if (index == 0 && (thisValue.IsUndefined() || thisValue.IsHole())) { + return ThrowReferenceError(thread, JSTaggedValue::Undefined(), "sub-class must call super before use 'this'"); + } + if (index == 1 && !thisValue.IsUndefined() && !thisValue.IsHole()) { + return ThrowReferenceError(thread, JSTaggedValue::Undefined(), "super() forbidden re-bind 'this'"); + } + return JSTaggedValue::True(); +} + +void SlowRuntimeStub::ThrowIfNotObject(JSThread *thread) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + THROW_TYPE_ERROR(thread, "Inner return result is not object"); +} + +void SlowRuntimeStub::ThrowThrowNotExists(JSThread *thread) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + THROW_TYPE_ERROR(thread, "Throw method is not defined"); +} + +void SlowRuntimeStub::ThrowPatternNonCoercible(JSThread *thread) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle msg(thread->GlobalConstants()->GetHandledObjNotCoercibleString()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + THROW_NEW_ERROR_AND_RETURN(thread, factory->NewJSError(base::ErrorType::TYPE_ERROR, msg).GetTaggedValue()); +} + +JSTaggedValue SlowRuntimeStub::StOwnByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, StOwnByNameDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, prop); + JSHandle valueHandle(thread, value); + ASSERT(propHandle->IsStringOrSymbol()); + + // property in class is non-enumerable + bool enumerable = !(objHandle->IsClassPrototype() || objHandle->IsClassConstructor()); + + PropertyDescriptor desc(thread, valueHandle, true, enumerable, true); + bool ret = JSTaggedValue::DefineOwnProperty(thread, objHandle, propHandle, desc); + if (!ret) { + return ThrowTypeError(thread, "SetOwnByName failed"); + } + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::StOwnByIndex(JSThread *thread, JSTaggedValue obj, JSTaggedValue idx, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, StOwnByIdDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSHandle idxHandle(thread, idx); + JSHandle valueHandle(thread, value); + + // property in class is non-enumerable + bool enumerable = !(objHandle->IsClassPrototype() || objHandle->IsClassConstructor()); + + PropertyDescriptor desc(thread, valueHandle, true, enumerable, true); + bool ret = JSTaggedValue::DefineOwnProperty(thread, objHandle, idxHandle, desc); + if (!ret) { + return ThrowTypeError(thread, "SetOwnByIndex failed"); + } + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::StOwnByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + INTERPRETER_TRACE(thread, StOwnByValueDyn); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle objHandle(thread, obj); + JSHandle keyHandle(thread, key); + JSHandle valueHandle(thread, value); + + if (objHandle->IsClassConstructor() && + JSTaggedValue::SameValue(keyHandle, globalConst->GetHandledPrototypeString())) { + return ThrowTypeError(thread, "In a class, static property named 'prototype' throw a TypeError"); + } + + // property in class is non-enumerable + bool enumerable = !(objHandle->IsClassPrototype() || objHandle->IsClassConstructor()); + + PropertyDescriptor desc(thread, valueHandle, true, enumerable, true); + JSMutableHandle propKey(JSTaggedValue::ToPropertyKey(thread, keyHandle)); + bool ret = JSTaggedValue::DefineOwnProperty(thread, objHandle, propKey, desc); + if (!ret) { + return ThrowTypeError(thread, "StOwnByValue failed"); + } + if (valueHandle->IsJSFunction()) { + if (propKey->IsNumber()) { + propKey.Update(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue()).GetTaggedValue()); + } + JSFunctionBase::SetFunctionName(thread, JSHandle::Cast(valueHandle), propKey, + JSHandle(thread, JSTaggedValue::Undefined())); + } + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::CreateEmptyArray(JSThread *thread, ObjectFactory *factory, JSHandle globalEnv) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle builtinObj(globalEnv->GetArrayFunction()); + JSHandle arr = factory->NewJSObjectByConstructor(builtinObj, JSHandle(builtinObj)); + return arr.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CreateEmptyObject(JSThread *thread, ObjectFactory *factory, + JSHandle globalEnv) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle builtinObj(globalEnv->GetObjectFunction()); + JSHandle obj = factory->NewJSObjectByConstructor(builtinObj, JSHandle(builtinObj)); + return obj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CreateObjectWithBuffer(JSThread *thread, ObjectFactory *factory, JSObject *literal) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle obj(thread, literal); + JSHandle objLiteral = factory->CloneObjectLiteral(obj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return objLiteral.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CreateObjectHavingMethod(JSThread *thread, ObjectFactory *factory, JSObject *literal, + JSTaggedValue env, ConstantPool *constpool) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle obj(thread, literal); + JSHandle objLiteral = factory->CloneObjectLiteral( + obj, JSHandle(thread, env), JSHandle(thread, JSTaggedValue(constpool))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return objLiteral.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::SetObjectWithProto(JSThread *thread, JSTaggedValue proto, JSTaggedValue obj) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + if (!proto.IsECMAObject() && !proto.IsNull()) { + return JSTaggedValue::False(); + } + JSHandle protoHandle(thread, proto); + JSHandle objHandle(thread, obj); + JSObject::SetPrototype(thread, objHandle, protoHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::IterNext(JSThread *thread, JSTaggedValue iter) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle iterHandle(thread, iter); + JSHandle resultObj = JSIterator::IteratorNext(thread, iterHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return resultObj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CloseIterator(JSThread *thread, JSTaggedValue iter) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + JSHandle iterHandle(thread, iter); + JSHandle record; + if (thread->GetException().IsObjectWrapper()) { + JSTaggedValue exception = ObjectWrapper::Cast(thread->GetException().GetTaggedObject())->GetValue(); + record = JSHandle(factory->NewCompletionRecord(CompletionRecord::THROW, + JSHandle(thread, exception))); + } else { + JSHandle undefinedVal = globalConst->GetHandledUndefined(); + record = JSHandle( + factory->NewCompletionRecord(CompletionRecord::NORMAL, undefinedVal)); + } + JSHandle result = JSIterator::IteratorClose(thread, iterHandle, record); + if (result->IsCompletionRecord()) { + return CompletionRecord::Cast(result->GetTaggedObject())->GetValue(); + } + return result.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::ImportModule([[maybe_unused]] JSThread *thread, + [[maybe_unused]] JSTaggedValue moduleName) +{ + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle name(thread, moduleName); + JSHandle module = thread->GetEcmaVM()->GetModuleByName(name); + return module.GetTaggedValue(); // return moduleRef +} + +void SlowRuntimeStub::StModuleVar([[maybe_unused]] JSThread *thread, [[maybe_unused]] JSTaggedValue exportName, + [[maybe_unused]] JSTaggedValue exportObj) +{ + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle name(thread, exportName); + JSHandle value(thread, exportObj); + thread->GetEcmaVM()->GetModuleManager()->AddModuleItem(thread, name, value); +} + +void SlowRuntimeStub::CopyModule(JSThread *thread, JSTaggedValue srcModule) +{ + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle srcModuleObj(thread, srcModule); + thread->GetEcmaVM()->GetModuleManager()->CopyModule(thread, srcModuleObj); +} + +JSTaggedValue SlowRuntimeStub::LdModvarByName([[maybe_unused]] JSThread *thread, + [[maybe_unused]] JSTaggedValue moduleObj, + [[maybe_unused]] JSTaggedValue itemName) +{ + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle module(thread, moduleObj); + JSHandle item(thread, itemName); + JSHandle moduleVar = thread->GetEcmaVM()->GetModuleManager()->GetModuleItem(thread, module, item); + return moduleVar.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CreateArrayWithBuffer(JSThread *thread, ObjectFactory *factory, JSArray *literal) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle array(thread, literal); + JSHandle arrLiteral = factory->CloneArrayLiteral(array); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return arrLiteral.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::GetTemplateObject(JSThread *thread, JSTaggedValue literal) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle templateLiteral(thread, literal); + JSHandle templateObj = TemplateString::GetTemplateObject(thread, templateLiteral); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return templateObj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::GetNextPropName(JSThread *thread, JSTaggedValue iter) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle iterator(thread, iter); + ASSERT(iterator->IsForinIterator()); + std::pair res = + JSForInIterator::NextInternal(thread, JSHandle::Cast(iterator)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res.first; +} + +JSTaggedValue SlowRuntimeStub::CopyDataProperties(JSThread *thread, JSTaggedValue dst, JSTaggedValue src) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle dstHandle(thread, dst); + JSHandle srcHandle(thread, src); + if (!srcHandle->IsNull() && !srcHandle->IsUndefined()) { + JSHandle keys = JSTaggedValue::GetOwnPropertyKeys(thread, srcHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + array_size_t keysLen = keys->GetLength(); + for (array_size_t i = 0; i < keysLen; i++) { + PropertyDescriptor desc(thread); + key.Update(keys->Get(i)); + bool success = JSTaggedValue::GetOwnProperty(thread, srcHandle, key, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (success && desc.IsEnumerable()) { + JSTaggedValue::DefineOwnProperty(thread, dstHandle, key, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + } + return dstHandle.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::GetIteratorNext(JSThread *thread, JSTaggedValue obj, JSTaggedValue method) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle iter(thread, obj); + JSHandle next(thread, method); + + JSHandle argv(factory->EmptyArray()); + ASSERT(next->IsCallable()); + JSTaggedValue ret = JSFunction::Call(thread, next, iter, argv); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!ret.IsECMAObject()) { + return ThrowTypeError(thread, "the Iterator is not an ecmaobject."); + } + return ret; +} + +JSTaggedValue SlowRuntimeStub::GetUnmapedArgs(JSThread *thread, JSTaggedType *sp, uint32_t actualNumArgs, + uint32_t startIdx) +{ + INTERPRETER_TRACE(thread, GetUnmappedArgs); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle argumentsList = factory->NewTaggedArray(actualNumArgs); + for (array_size_t i = 0; i < actualNumArgs; ++i) { + argumentsList->Set(thread, i, + JSTaggedValue(sp[startIdx + i])); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + // 1. Let len be the number of elements in argumentsList + int32_t len = argumentsList->GetLength(); + // 2. Let obj be ObjectCreate(%ObjectPrototype%, «[[ParameterMap]]»). + // 3. Set obj’s [[ParameterMap]] internal slot to undefined. + JSHandle obj = factory->NewJSArguments(); + // 4. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor{[[Value]]: len, [[Writable]]: true, + // [[Enumerable]]: false, [[Configurable]]: true}). + obj->SetPropertyInlinedProps(thread, JSArguments::LENGTH_INLINE_PROPERTY_INDEX, JSTaggedValue(len)); + // 5. Let index be 0. + // 6. Repeat while index < len, + // a. Let val be argumentsList[index]. + // b. Perform CreateDataProperty(obj, ToString(index), val). + // c. Let index be index + 1 + obj->SetElements(thread, argumentsList.GetTaggedValue()); + // 7. Perform DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor + // {[[Value]]:%ArrayProto_values%, + // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}). + obj->SetPropertyInlinedProps(thread, JSArguments::ITERATOR_INLINE_PROPERTY_INDEX, + globalEnv->GetArrayProtoValuesFunction().GetTaggedValue()); + // 8. Perform DefinePropertyOrThrow(obj, "caller", PropertyDescriptor {[[Get]]: %ThrowTypeError%, + // [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}). + JSHandle throwFunction = globalEnv->GetThrowTypeError(); + JSHandle accessor = factory->NewAccessorData(); + accessor->SetGetter(thread, throwFunction); + accessor->SetSetter(thread, throwFunction); + obj->SetPropertyInlinedProps(thread, JSArguments::CALLER_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + // 9. Perform DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]: %ThrowTypeError%, + // [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}). + accessor = factory->NewAccessorData(); + accessor->SetGetter(thread, throwFunction); + accessor->SetSetter(thread, throwFunction); + obj->SetPropertyInlinedProps(thread, JSArguments::CALLEE_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11. Return obj + return obj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CopyRestArgs(JSThread *thread, JSTaggedType *sp, uint32_t restNumArgs, uint32_t startIdx) +{ + INTERPRETER_TRACE(thread, Copyrestargs); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle restArray = JSArray::ArrayCreate(thread, JSTaggedNumber(restNumArgs)); + + JSMutableHandle element(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < restNumArgs; ++i) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + element.Update(JSTaggedValue(sp[startIdx + i])); + JSObject::SetProperty(thread, restArray, i, element, true); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return restArray.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::GetIterator(JSThread *thread, JSTaggedValue obj) +{ + INTERPRETER_TRACE(thread, GetIterator); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle objHandle(thread, obj); + JSHandle valuesFunc = + JSTaggedValue::GetProperty(thread, objHandle, env->GetIteratorSymbol()).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!valuesFunc->IsCallable()) { + return valuesFunc.GetTaggedValue(); + } + JSHandle newTarget(thread, JSTaggedValue::Undefined()); + JSHandle args = vm->GetFactory()->EmptyArray(); + JSTaggedValue res = InvokeJsFunction(thread, JSHandle(valuesFunc), objHandle, newTarget, args); + + return res; +} + +JSTaggedValue SlowRuntimeStub::DefineGetterSetterByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, + JSTaggedValue getter, JSTaggedValue setter, bool flag) +{ + INTERPRETER_TRACE(thread, DefineGetterSetterByValue); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, prop); + + JSHandle getterHandle(thread, getter); + JSHandle setterHandle(thread, setter); + JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, propHandle); + + auto globalConst = thread->GlobalConstants(); + if (objHandle.GetTaggedValue().IsClassConstructor() && + JSTaggedValue::SameValue(propKey, globalConst->GetHandledPrototypeString())) { + return ThrowTypeError( + thread, + "In a class, computed property names for static getter that are named 'prototype' throw a TypeError"); + } + + if (flag) { + if (!getterHandle->IsUndefined()) { + if (propKey->IsNumber()) { + propKey = + JSHandle::Cast(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue())); + } + JSFunctionBase::SetFunctionName(thread, JSHandle::Cast(getterHandle), propKey, + JSHandle(thread, globalConst->GetGetString())); + } + + if (!setterHandle->IsUndefined()) { + if (propKey->IsNumber()) { + propKey = + JSHandle::Cast(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue())); + } + JSFunctionBase::SetFunctionName(thread, JSHandle::Cast(setterHandle), propKey, + JSHandle(thread, globalConst->GetSetString())); + } + } + + // set accessor + bool enumerable = + !(objHandle.GetTaggedValue().IsClassPrototype() || objHandle.GetTaggedValue().IsClassConstructor()); + PropertyDescriptor desc(thread, true, enumerable, true); + if (!getterHandle->IsUndefined()) { + JSHandle::Cast(getterHandle)->SetFunctionKind(thread, FunctionKind::GETTER_FUNCTION); + desc.SetGetter(getterHandle); + } + if (!setterHandle->IsUndefined()) { + JSHandle::Cast(setterHandle)->SetFunctionKind(thread, FunctionKind::SETTER_FUNCTION); + desc.SetSetter(setterHandle); + } + JSObject::DefineOwnProperty(thread, objHandle, propKey, desc); + + return objHandle.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::LdObjByIndex(JSThread *thread, JSTaggedValue obj, JSTaggedValue idx, bool callGetter, + JSTaggedValue receiver) +{ + INTERPRETER_TRACE(thread, LdObjByIndexDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSTaggedValue res; + JSHandle objHandle(thread, obj); + if (callGetter) { + res = JSObject::CallGetter(thread, AccessorData::Cast(receiver.GetTaggedObject()), objHandle); + } else { + res = JSTaggedValue::GetProperty(thread, objHandle, idx.GetArrayLength()).GetValue().GetTaggedValue(); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res; +} + +JSTaggedValue SlowRuntimeStub::StObjByIndex(JSThread *thread, JSTaggedValue obj, JSTaggedValue idx, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, StObjByIndexDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSTaggedValue::SetProperty(thread, JSHandle(thread, obj), idx.GetArrayLength(), + JSHandle(thread, value), true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::LdObjByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, bool callGetter, + JSTaggedValue receiver) +{ + INTERPRETER_TRACE(thread, LdObjByNameDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSTaggedValue res; + if (callGetter) { + res = JSObject::CallGetter(thread, AccessorData::Cast(receiver.GetTaggedObject()), objHandle); + } else { + JSHandle propHandle(thread, prop); + res = JSTaggedValue::GetProperty(thread, objHandle, propHandle).GetValue().GetTaggedValue(); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res; +} + +JSTaggedValue SlowRuntimeStub::StObjByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, StObjByNameDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, prop); + JSHandle valueHandle(thread, value); + JSTaggedValue::SetProperty(thread, objHandle, propHandle, valueHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::LdObjByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, bool callGetter, + JSTaggedValue receiver) +{ + INTERPRETER_TRACE(thread, LdObjByValueDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSTaggedValue res; + if (callGetter) { + res = JSObject::CallGetter(thread, AccessorData::Cast(receiver.GetTaggedObject()), objHandle); + } else { + JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, JSHandle(thread, prop)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + res = JSTaggedValue::GetProperty(thread, objHandle, propKey).GetValue().GetTaggedValue(); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res; +} + +JSTaggedValue SlowRuntimeStub::StObjByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, + JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, StObjByValueDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, prop); + JSHandle valueHandle(thread, value); + JSHandle propKey(JSTaggedValue::ToPropertyKey(thread, propHandle)); + + // strict mode is true + JSTaggedValue::SetProperty(thread, objHandle, propKey, valueHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::TryLdGlobalByName(JSThread *thread, JSTaggedValue global, JSTaggedValue prop) +{ + INTERPRETER_TRACE(thread, Trygetobjprop); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle obj(thread, global.GetTaggedObject()->GetClass()->GetPrototype()); + JSHandle propHandle(thread, prop); + OperationResult res = JSTaggedValue::GetProperty(thread, obj, propHandle); + if (!res.GetPropertyMetaData().IsFound()) { + return ThrowReferenceError(thread, prop, " is not defined"); + } + return res.GetValue().GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::TryStGlobalByName(JSThread *thread, JSTaggedValue prop) +{ + // If fast path is fail, not need slow path, just throw error. + return ThrowReferenceError(thread, prop, " is not defined"); +} + +JSTaggedValue SlowRuntimeStub::LdGlobalVar(JSThread *thread, JSTaggedValue global, JSTaggedValue prop) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, global.GetTaggedObject()->GetClass()->GetPrototype()); + JSHandle propHandle(thread, prop); + OperationResult res = JSTaggedValue::GetProperty(thread, objHandle, propHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res.GetValue().GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::StGlobalVar(JSThread *thread, JSTaggedValue prop, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle global(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()); + JSHandle propHandle(thread, prop); + JSHandle valueHandle(thread, value); + + JSObject::GlobalSetProperty(thread, propHandle, valueHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::ThrowReferenceError(JSThread *thread, JSTaggedValue prop, const char *desc) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle propName = JSTaggedValue::ToString(thread, JSHandle(thread, prop)); + ASSERT_NO_ABRUPT_COMPLETION(thread); + JSHandle info = factory->NewFromString(desc); + JSHandle msg = factory->ConcatFromString(propName, info); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, + factory->NewJSError(base::ErrorType::REFERENCE_ERROR, msg).GetTaggedValue(), + JSTaggedValue::Exception()); +} + +JSTaggedValue SlowRuntimeStub::ThrowTypeError(JSThread *thread, const char *message) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ASSERT_NO_ABRUPT_COMPLETION(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, message, JSTaggedValue::Exception()); +} + +JSTaggedValue SlowRuntimeStub::StArraySpread(JSThread *thread, JSTaggedValue dst, JSTaggedValue index, + JSTaggedValue src) +{ + INTERPRETER_TRACE(thread, StArraySpread); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle dstHandle(thread, dst); + JSHandle srcHandle(thread, src); + ASSERT(dstHandle->IsJSArray() && !srcHandle->IsNull() && !srcHandle->IsUndefined()); + if (srcHandle->IsString()) { + JSHandle srcString = JSTaggedValue::ToString(thread, srcHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + array_size_t dstLen = index.GetInt(); + array_size_t strLen = srcString->GetLength(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + for (array_size_t i = 0; i < strLen; i++) { + uint16_t res = srcString->At(i); + JSHandle strValue(factory->NewFromUtf16Literal(&res, 1)); + JSTaggedValue::SetProperty(thread, dstHandle, dstLen + i, strValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + return JSTaggedValue(dstLen + strLen); + } + JSHandle keys = JSTaggedValue::GetOwnPropertyKeys(thread, srcHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSMutableHandle indexHandle(thread, index); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + array_size_t length = keys->GetLength(); + for (array_size_t i = 0; i < length; i++) { + PropertyDescriptor desc(thread); + key.Update(keys->Get(i)); + bool success = JSTaggedValue::GetOwnProperty(thread, srcHandle, key, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (success && desc.IsEnumerable()) { + JSTaggedValue::DefineOwnProperty(thread, dstHandle, indexHandle, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int tmp = indexHandle->GetInt(); + indexHandle.Update(JSTaggedValue(tmp + 1)); + } + } + + return indexHandle.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::DefineGeneratorFunc(JSThread *thread, JSFunction *func) +{ + INTERPRETER_TRACE(thread, DefineGeneratorFunc); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto method = func->GetCallTarget(); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle jsFunc = factory->NewJSGeneratorFunction(method); + ASSERT_NO_ABRUPT_COMPLETION(thread); + + // 26.3.4.3 prototype + // Whenever a GeneratorFunction instance is created another ordinary object is also created and + // is the initial value of the generator function's "prototype" property. + JSHandle objFun = env->GetObjectFunction(); + JSHandle initialGeneratorFuncPrototype = + factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSObject::SetPrototype(thread, initialGeneratorFuncPrototype, env->GetGeneratorPrototype()); + ASSERT_NO_ABRUPT_COMPLETION(thread); + jsFunc->SetProtoOrDynClass(thread, initialGeneratorFuncPrototype); + + return jsFunc.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::DefineAsyncFunc(JSThread *thread, JSFunction *func) +{ + INTERPRETER_TRACE(thread, DefineAsyncFunc); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto method = func->GetCallTarget(); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass = JSHandle::Cast(env->GetAsyncFunctionClass()); + JSHandle jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::ASYNC_FUNCTION); + ASSERT_NO_ABRUPT_COMPLETION(thread); + return jsFunc.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::DefineNCFuncDyn(JSThread *thread, JSFunction *func) +{ + INTERPRETER_TRACE(thread, DefineNCFuncDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto method = func->GetCallTarget(); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass = JSHandle::Cast(env->GetFunctionClassWithoutProto()); + JSHandle jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::NORMAL_FUNCTION); + ASSERT_NO_ABRUPT_COMPLETION(thread); + return jsFunc.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::DefinefuncDyn(JSThread *thread, JSFunction *func) +{ + INTERPRETER_TRACE(thread, DefinefuncDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto method = func->GetCallTarget(); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass = JSHandle::Cast(env->GetFunctionClassWithProto()); + JSHandle jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::BASE_CONSTRUCTOR); + ASSERT_NO_ABRUPT_COMPLETION(thread); + return jsFunc.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::NewClassFunc(JSThread *thread, JSFunction *func) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto method = func->GetCallTarget(); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass = JSHandle::Cast(env->GetFunctionClassWithoutName()); + JSHandle jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::CLASS_CONSTRUCTOR); + ASSERT_NO_ABRUPT_COMPLETION(thread); + return jsFunc.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::DefineClass(JSThread *thread, JSFunction *func, TaggedArray *literal, + JSTaggedValue proto, JSTaggedValue lexenv, ConstantPool *constpool) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle cls(thread, func); + ASSERT(cls->IsJSFunction()); + JSHandle literalBuffer(thread, literal); + JSMutableHandle parent(thread, proto); + JSHandle lexicalEnv(thread, lexenv); + JSHandle constantpool(thread, constpool); + + cls->GetTaggedObject()->GetClass()->SetClassConstructor(true); + JSFunction::Cast(cls->GetTaggedObject())->SetClassConstructor(thread, true); + + /* + * set class __proto__ + * + * class A / class A extends null class A extends B + * a a + * | | + * | __proto__ | __proto__ + * | | + * A ----> A.prototype A ----> A.prototype + * | | | | + * | __proto__ | __proto__ | __proto__ | __proto__ + * | | | | + * Function.prototype Object.prototype / null B ----> B.prototype + */ + JSMutableHandle parentPrototype(thread, JSTaggedValue::Undefined()); + // hole means parent is not present + if (parent->IsHole()) { + JSHandle::Cast(cls)->SetFunctionKind(thread, FunctionKind::CLASS_CONSTRUCTOR); + parentPrototype.Update(env->GetObjectFunctionPrototype().GetTaggedValue()); + parent.Update(env->GetFunctionPrototype().GetTaggedValue()); + } else if (parent->IsNull()) { + JSHandle::Cast(cls)->SetFunctionKind(thread, FunctionKind::DERIVED_CONSTRUCTOR); + parentPrototype.Update(JSTaggedValue::Null()); + parent.Update(env->GetFunctionPrototype().GetTaggedValue()); + } else if (!parent->IsConstructor()) { + return ThrowTypeError(thread, "parent class is not constructor"); + } else { + JSHandle::Cast(cls)->SetFunctionKind(thread, FunctionKind::DERIVED_CONSTRUCTOR); + parentPrototype.Update(JSTaggedValue::GetProperty(thread, parent, + globalConst->GetHandledPrototypeString()).GetValue().GetTaggedValue()); + if (!parentPrototype->IsECMAObject() && !parentPrototype->IsNull()) { + return ThrowTypeError(thread, "parent class have no valid prototype"); + } + } + + // null cannot cast JSObject + JSHandle clsPrototype = JSObject::ObjectCreate(thread, JSHandle(parentPrototype)); + + bool success = true; + success = success && JSFunction::MakeClassConstructor(thread, cls, clsPrototype); + clsPrototype.GetTaggedValue().GetTaggedObject()->GetClass()->SetClassPrototype(true); + + success = success && JSObject::SetPrototype(thread, JSHandle::Cast(cls), parent); + + uint32_t bufferLength = literalBuffer->GetLength(); + // static location is hidden in the last index of Literal buffer + uint32_t staticLoc = literalBuffer->Get(thread, bufferLength - 1).GetInt(); + + // set property on class.prototype and class + JSMutableHandle propKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle propValue(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < staticLoc * 2; i += 2) { // 2: Each literal buffer contains a pair of key-value. + // set non-static property on cls.prototype + propKey.Update(literalBuffer->Get(thread, i)); + propValue.Update(literalBuffer->Get(thread, i + 1)); + if (propValue->IsJSFunction()) { + propValue.Update( + factory->CloneJSFuction(JSHandle::Cast(propValue), FunctionKind::NORMAL_FUNCTION) + .GetTaggedValue()); + JSHandle propFunc = JSHandle::Cast(propValue); + propFunc->SetHomeObject(thread, clsPrototype); + propFunc->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue()); + propFunc->SetConstantPool(thread, constantpool.GetTaggedValue()); + } + PropertyDescriptor desc(thread, propValue, true, false, true); // non-enumerable + success = success && JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle::Cast(clsPrototype), + propKey, desc); + } + for (uint32_t i = staticLoc * 2; i < bufferLength - 1; i += 2) { // 2: ditto + // set static property on cls + propKey.Update(literalBuffer->Get(thread, i)); + propValue.Update(literalBuffer->Get(thread, i + 1)); + if (propValue->IsJSFunction()) { + propValue.Update( + factory->CloneJSFuction(JSHandle::Cast(propValue), FunctionKind::NORMAL_FUNCTION) + .GetTaggedValue()); + JSHandle propFunc = JSHandle::Cast(propValue); + propFunc->SetHomeObject(thread, cls); + propFunc->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue()); + propFunc->SetConstantPool(thread, constantpool.GetTaggedValue()); + } + PropertyDescriptor desc(thread, propValue, true, false, true); // non-enumerable + success = success && JSTaggedValue::DefinePropertyOrThrow(thread, cls, propKey, desc); + } + + // ECMA2015 14.5.15: if class don't have a method which is name(), set the class a string name. + if (!JSTaggedValue::HasOwnProperty(thread, cls, globalConst->GetHandledNameString())) { + JSMethod *clsTarget = JSHandle::Cast(cls)->GetCallTarget(); + ASSERT(clsTarget != nullptr); + CString clsName( + utf::Mutf8AsCString(clsTarget->GetStringDataAnnotation(Method::AnnotationField::FUNCTION_NAME).data)); + if (!clsName.empty()) { + success = + success && JSFunction::SetFunctionName(thread, JSHandle(cls), + JSHandle(factory->NewFromString(clsName.c_str())), + globalConst->GetHandledUndefined()); + } + } + + if (!success) { + return JSTaggedValue::Exception(); + } + + ASSERT_NO_ABRUPT_COMPLETION(thread); + return cls.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::SuperCall(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, + uint16_t firstVRegIdx, uint16_t length) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + EcmaFrameHandler frameHandler(thread); + + JSHandle funcHandle(thread, func); + JSHandle newTargetHandle(thread, newTarget); + + JSHandle superFunc(thread, JSHandle::Cast(funcHandle)->GetPrototype(thread)); + ASSERT(superFunc->IsJSFunction()); + + JSHandle argv = factory->NewTaggedArray(length); + for (size_t i = 0; i < length; ++i) { + argv->Set(thread, i, frameHandler.GetVRegValue(firstVRegIdx + i)); + } + JSTaggedValue result = JSFunction::Construct(thread, superFunc, argv, newTargetHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return result; +} + +JSTaggedValue SlowRuntimeStub::SuperCallSpread(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, + JSTaggedValue array) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaFrameHandler frameHandler(thread); + + JSHandle funcHandle(thread, func); + JSHandle newTargetHandle(thread, newTarget); + JSHandle jsArray(thread, array); + + JSHandle superFunc(thread, JSHandle::Cast(funcHandle)->GetPrototype(thread)); + ASSERT(superFunc->IsJSFunction()); + + JSHandle argv(thread, GetCallSpreadArgs(thread, jsArray.GetTaggedValue())); + JSTaggedValue result = JSFunction::Construct(thread, superFunc, argv, newTargetHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return result; +} + +JSTaggedValue SlowRuntimeStub::DefineMethod(JSThread *thread, JSFunction *func, JSTaggedValue homeObject) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ASSERT(homeObject.IsECMAObject()); + JSHandle homeObjectHandle(thread, homeObject); + auto method = func->GetCallTarget(); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass = JSHandle::Cast(env->GetFunctionClassWithoutProto()); + JSHandle jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::NORMAL_FUNCTION); + jsFunc->SetHomeObject(thread, homeObjectHandle); + ASSERT_NO_ABRUPT_COMPLETION(thread); + return jsFunc.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::LdSuperByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, + JSTaggedValue thisFunc) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ASSERT(thisFunc.IsJSFunction()); + // get Homeobject form function + JSHandle homeObject(thread, JSFunction::Cast(thisFunc.GetTaggedObject())->GetHomeObject()); + + if (obj.IsUndefined()) { + return ThrowReferenceError(thread, obj, "this is uninitialized."); + } + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, key); + + JSHandle propKey(JSTaggedValue::ToPropertyKey(thread, propHandle)); + JSHandle superBase(thread, JSTaggedValue::GetSuperBase(thread, homeObject)); + JSTaggedValue::RequireObjectCoercible(thread, superBase); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSTaggedValue res = JSTaggedValue::GetProperty(thread, superBase, propKey, objHandle).GetValue().GetTaggedValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res; +} + +JSTaggedValue SlowRuntimeStub::StSuperByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, + JSTaggedValue value, JSTaggedValue thisFunc) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ASSERT(thisFunc.IsJSFunction()); + // get Homeobject form function + JSHandle homeObject(thread, JSFunction::Cast(thisFunc.GetTaggedObject())->GetHomeObject()); + + if (obj.IsUndefined()) { + return ThrowReferenceError(thread, obj, "this is uninitialized."); + } + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, key); + JSHandle valueHandle(thread, value); + + JSHandle propKey(JSTaggedValue::ToPropertyKey(thread, propHandle)); + JSHandle superBase(thread, JSTaggedValue::GetSuperBase(thread, homeObject)); + JSTaggedValue::RequireObjectCoercible(thread, superBase); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // check may_throw is false? + JSTaggedValue::SetProperty(thread, superBase, propKey, valueHandle, objHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::GetCallSpreadArgs(JSThread *thread, JSTaggedValue array) +{ + INTERPRETER_TRACE(thread, GetCallSpreadArgs); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle jsArray(thread, array); + uint32_t argvMayMaxLength = JSHandle::Cast(jsArray)->GetArrayLength(); + JSHandle argv = factory->NewTaggedArray(argvMayMaxLength); + JSHandle itor = JSIterator::GetIterator(thread, jsArray); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSMutableHandle next(thread, JSTaggedValue::Undefined()); + JSMutableHandle nextArg(thread, JSTaggedValue::Undefined()); + size_t argvIndex = 0; + while (true) { + next.Update(JSIterator::IteratorStep(thread, itor).GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (JSTaggedValue::SameValue(next.GetTaggedValue(), JSTaggedValue::False())) { + break; + } + nextArg.Update(JSIterator::IteratorValue(thread, next).GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + argv->Set(thread, argvIndex++, nextArg); + } + + argv = factory->CopyArray(argv, argvMayMaxLength, argvIndex); + return argv.GetTaggedValue(); +} + +void SlowRuntimeStub::ThrowDeleteSuperProperty(JSThread *thread) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle info = factory->NewFromString("Can not delete super property"); + JSHandle errorObj = factory->NewJSError(base::ErrorType::REFERENCE_ERROR, info); + THROW_NEW_ERROR_AND_RETURN(thread, errorObj.GetTaggedValue()); +} + +JSTaggedValue SlowRuntimeStub::NotifyInlineCache(JSThread *thread, JSFunction *func, JSMethod *method) +{ + // use vtable index as ic Size + uint32_t icSlotSize = method->GetNumericalAnnotation(JSMethod::AnnotationField::IC_SIZE); + if (icSlotSize > 0 && icSlotSize < ProfileTypeInfo::INVALID_SLOT_INDEX) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle funcHandle(thread, func); + JSHandle profileTypeInfo = factory->NewProfileTypeInfo(icSlotSize); + funcHandle->SetProfileTypeInfo(thread, profileTypeInfo.GetTaggedValue()); + return profileTypeInfo.GetTaggedValue(); + } + return JSTaggedValue::Undefined(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/interpreter/slow_runtime_stub.h b/ecmascript/interpreter/slow_runtime_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..b67e7d3fc39684b432e6fc591e47f971962fad7f --- /dev/null +++ b/ecmascript/interpreter/slow_runtime_stub.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_SLOW_RUNTIME_STUB_H +#define PANDA_RUNTIME_ECMASCRIPT_SLOW_RUNTIME_STUB_H + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" + +namespace panda::ecmascript { +class GlobalEnv; +class JSArray; + +class SlowRuntimeStub { +public: + /* -------------- Common API Begin, Don't change those interface!!! ----------------- */ + static JSTaggedValue CallSpreadDyn(JSThread *thread, JSTaggedValue func, JSTaggedValue obj, JSTaggedValue array); + static JSTaggedValue NegDyn(JSThread *thread, JSTaggedValue value); + static JSTaggedValue AsyncFunctionEnter(JSThread *thread); + static JSTaggedValue ToNumber(JSThread *thread, JSTaggedValue value); + static JSTaggedValue NotDyn(JSThread *thread, JSTaggedValue value); + static JSTaggedValue IncDyn(JSThread *thread, JSTaggedValue value); + static JSTaggedValue DecDyn(JSThread *thread, JSTaggedValue value); + static void ThrowDyn(JSThread *thread, JSTaggedValue value); + static JSTaggedValue GetPropIterator(JSThread *thread, JSTaggedValue value); + static void ThrowConstAssignment(JSThread *thread, JSTaggedValue value); + static JSTaggedValue Add2Dyn(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue Sub2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue Mul2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue Div2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue Mod2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue EqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue NotEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue LessDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue LessEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue GreaterDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue GreaterEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + + static JSTaggedValue ToJSTaggedValueWithInt32(JSThread *thread, JSTaggedValue value); + static JSTaggedValue ToJSTaggedValueWithUint32(JSThread *thread, JSTaggedValue value); + + static JSTaggedValue DelObjProp(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop); + static JSTaggedValue NewObjDynRange(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, + uint16_t firstArgIdx, uint16_t length); + static JSTaggedValue CreateObjectWithExcludedKeys(JSThread *thread, uint16_t numKeys, JSTaggedValue objVal, + uint16_t firstArgRegIdx); + static JSTaggedValue ExpDyn(JSThread *thread, JSTaggedValue base, JSTaggedValue exponent); + static JSTaggedValue IsInDyn(JSThread *thread, JSTaggedValue prop, JSTaggedValue obj); + static JSTaggedValue InstanceofDyn(JSThread *thread, JSTaggedValue obj, JSTaggedValue target); + + static JSTaggedValue NewLexicalEnvDyn(JSThread *thread, uint16_t numVars); + static JSTaggedValue CreateIterResultObj(JSThread *thread, JSTaggedValue value, JSTaggedValue flag); + + static JSTaggedValue CreateGeneratorObj(JSThread *thread, JSTaggedValue genFunc); + static JSTaggedValue SuspendGenerator(JSThread *thread, JSTaggedValue genObj, JSTaggedValue value); + static JSTaggedValue AsyncFunctionAwaitUncaught(JSThread *thread, JSTaggedValue asyncFuncObj, JSTaggedValue value); + static JSTaggedValue AsyncFunctionResolveOrReject(JSThread *thread, JSTaggedValue asyncFuncObj, JSTaggedValue value, + bool is_resolve); + + static JSTaggedValue NewObjSpreadDyn(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, + JSTaggedValue array); + static void ThrowUndefinedIfHole(JSThread *thread, JSTaggedValue obj); + static void ThrowIfNotObject(JSThread *thread); + static void ThrowThrowNotExists(JSThread *thread); + static void ThrowPatternNonCoercible(JSThread *thread); + static JSTaggedValue ThrowIfSuperNotCorrectCall(JSThread *thread, uint16_t index, JSTaggedValue thisValue); + static void ThrowDeleteSuperProperty(JSThread *thread); + + static JSTaggedValue StOwnByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, JSTaggedValue value); + static JSTaggedValue StOwnByIndex(JSThread *thread, JSTaggedValue obj, JSTaggedValue idx, JSTaggedValue value); + static JSTaggedValue StOwnByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, JSTaggedValue value); + static JSTaggedValue CreateEmptyArray(JSThread *thread, ObjectFactory *factory, JSHandle globalEnv); + static JSTaggedValue CreateEmptyObject(JSThread *thread, ObjectFactory *factory, JSHandle globalEnv); + static JSTaggedValue CreateObjectWithBuffer(JSThread *thread, ObjectFactory *factory, JSObject *literal); + static JSTaggedValue CreateObjectHavingMethod(JSThread *thread, ObjectFactory *factory, JSObject *literal, + JSTaggedValue env, ConstantPool *constpool); + static JSTaggedValue SetObjectWithProto(JSThread *thread, JSTaggedValue proto, JSTaggedValue obj); + static JSTaggedValue CreateArrayWithBuffer(JSThread *thread, ObjectFactory *factory, JSArray *literal); + + static JSTaggedValue GetTemplateObject(JSThread *thread, JSTaggedValue literal); + static JSTaggedValue GetNextPropName(JSThread *thread, JSTaggedValue iter); + static JSTaggedValue CopyDataProperties(JSThread *thread, JSTaggedValue dst, JSTaggedValue src); + + static JSTaggedValue GetUnmapedArgs(JSThread *thread, JSTaggedType *sp, uint32_t actualNumArgs, uint32_t startIdx); + static JSTaggedValue CopyRestArgs(JSThread *thread, JSTaggedType *sp, uint32_t restNumArgs, uint32_t startIdx); + static JSTaggedValue GetIterator(JSThread *thread, JSTaggedValue obj); + static JSTaggedValue IterNext(JSThread *thread, JSTaggedValue iter); + static JSTaggedValue CloseIterator(JSThread *thread, JSTaggedValue iter); + static JSTaggedValue ImportModule(JSThread *thread, JSTaggedValue moduleName); + static void StModuleVar(JSThread *thread, JSTaggedValue exportName, JSTaggedValue exportObj); + static void CopyModule(JSThread *thread, JSTaggedValue srcModule); + static JSTaggedValue LdModvarByName(JSThread *thread, JSTaggedValue moduleObj, JSTaggedValue itemName); + static JSTaggedValue GetIteratorNext(JSThread *thread, JSTaggedValue obj, JSTaggedValue method); + + static JSTaggedValue DefineGetterSetterByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, + JSTaggedValue getter, JSTaggedValue setter, bool flag); + + static JSTaggedValue LdObjByIndex(JSThread *thread, JSTaggedValue obj, JSTaggedValue idx, bool callGetter, + JSTaggedValue receiver); + static JSTaggedValue StObjByIndex(JSThread *thread, JSTaggedValue obj, JSTaggedValue idx, JSTaggedValue value); + static JSTaggedValue LdObjByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, bool callGetter, + JSTaggedValue receiver); + static JSTaggedValue StObjByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, JSTaggedValue value); + static JSTaggedValue LdObjByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, bool callGetter, + JSTaggedValue receiver); + static JSTaggedValue StObjByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, JSTaggedValue value); + static JSTaggedValue TryLdGlobalByName(JSThread *thread, JSTaggedValue global, JSTaggedValue prop); + static JSTaggedValue TryStGlobalByName(JSThread *thread, JSTaggedValue prop); + static JSTaggedValue LdGlobalVar(JSThread *thread, JSTaggedValue global, JSTaggedValue prop); + static JSTaggedValue StGlobalVar(JSThread *thread, JSTaggedValue prop, JSTaggedValue value); + static JSTaggedValue StArraySpread(JSThread *thread, JSTaggedValue dst, JSTaggedValue index, JSTaggedValue src); + + static JSTaggedValue DefineGeneratorFunc(JSThread *thread, JSFunction *func); + static JSTaggedValue DefineAsyncFunc(JSThread *thread, JSFunction *func); + static JSTaggedValue DefineNCFuncDyn(JSThread *thread, JSFunction *func); + static JSTaggedValue DefinefuncDyn(JSThread *thread, JSFunction *func); + static JSTaggedValue NewClassFunc(JSThread *thread, JSFunction *func); + + static JSTaggedValue DefineClass(JSThread *thread, JSFunction *func, TaggedArray *literal, JSTaggedValue proto, + JSTaggedValue lexenv, ConstantPool *constpool); + static JSTaggedValue SuperCall(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, uint16_t firstVRegIdx, + uint16_t length); + static JSTaggedValue SuperCallSpread(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, + JSTaggedValue array); + static JSTaggedValue DefineMethod(JSThread *thread, JSFunction *func, JSTaggedValue homeObject); + static JSTaggedValue LdSuperByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, JSTaggedValue thisFunc); + static JSTaggedValue StSuperByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, JSTaggedValue value, + JSTaggedValue thisFunc); + static JSTaggedValue NotifyInlineCache(JSThread *thread, JSFunction *func, JSMethod *method); + static JSTaggedValue ThrowReferenceError(JSThread *thread, JSTaggedValue prop, const char *desc); + /* -------------- Common API End, Don't change those interface!!! ----------------- */ + +private: + static JSTaggedValue ThrowTypeError(JSThread *thread, const char *message); + static JSTaggedValue GetCallSpreadArgs(JSThread *thread, JSTaggedValue array); +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_SLOW_RUNTIME_STUB_H diff --git a/ecmascript/interpreter/templates/debugger_instruction_dispatch.inl b/ecmascript/interpreter/templates/debugger_instruction_dispatch.inl new file mode 100644 index 0000000000000000000000000000000000000000..e27787db30dbd5b9711df033c485e265372bf957 --- /dev/null +++ b/ecmascript/interpreter/templates/debugger_instruction_dispatch.inl @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + &&DEBUG_HANDLE_MOV_DYN_V8_V8, + &&DEBUG_HANDLE_MOV_DYN_V16_V16, + &&DEBUG_HANDLE_LDA_STR_ID32, + &&DEBUG_HANDLE_LDAI_DYN_IMM32, + &&DEBUG_HANDLE_FLDAI_DYN_IMM64, + &&DEBUG_HANDLE_BUILTIN_LDNAN_IMM8, + &&DEBUG_HANDLE_BUILTIN_LDINFINITY_IMM8, + &&DEBUG_HANDLE_BUILTIN_LDGLOBALTHIS_IMM8, + &&DEBUG_HANDLE_BUILTIN_LDUNDEFINED_IMM8, + &&DEBUG_HANDLE_BUILTIN_LDNULL_IMM8, + &&DEBUG_HANDLE_BUILTIN_LDSYMBOL_IMM8, + &&DEBUG_HANDLE_BUILTIN_LDGLOBAL_IMM8, + &&DEBUG_HANDLE_BUILTIN_LDTRUE_IMM8, + &&DEBUG_HANDLE_BUILTIN_LDFALSE_IMM8, + &&DEBUG_HANDLE_BUILTIN_THROWDYN_IMM8, + &&DEBUG_HANDLE_BUILTIN_TYPEOFDYN_IMM8, + &&DEBUG_HANDLE_BUILTIN_LDLEXENVDYN_IMM8, + &&DEBUG_HANDLE_BUILTIN_POPLEXENVDYN_IMM8, + &&DEBUG_HANDLE_BUILTIN_GETUNMAPPEDARGS_IMM8, + &&DEBUG_HANDLE_BUILTIN_TOBOOLEAN_IMM8, + &&DEBUG_HANDLE_BUILTIN_GETPROPITERATOR_IMM8, + &&DEBUG_HANDLE_BUILTIN_ASYNCFUNCTIONENTER_IMM8, + &&DEBUG_HANDLE_BUILTIN_LDHOLE_IMM8, + &&DEBUG_HANDLE_BUILTIN_RETURNUNDEFINED_IMM8, + &&DEBUG_HANDLE_BUILTIN_CREATEEMPTYOBJECT_IMM8, + &&DEBUG_HANDLE_BUILTIN_CREATEEMPTYARRAY_IMM8, + &&DEBUG_HANDLE_BUILTIN_GETITERATOR_IMM8, + &&DEBUG_HANDLE_BUILTIN_THROWTHROWNOTEXISTS_IMM8, + &&DEBUG_HANDLE_BUILTIN_THROWPATTERNNONCOERCIBLE_IMM8, + &&DEBUG_HANDLE_BUILTIN_LDHOMEOBJECT_IMM8, + &&DEBUG_HANDLE_BUILTIN_THROWDELETESUPERPROPERTY_IMM8, + &&DEBUG_HANDLE_BUILTIN_DEBUGGER_IMM8, + &&DEBUG_HANDLE_JMP_IMM8, + &&DEBUG_HANDLE_JMP_IMM16, + &&DEBUG_HANDLE_JMP_IMM32, + &&DEBUG_HANDLE_JEQZ_IMM8, + &&DEBUG_HANDLE_JEQZ_IMM16, + &&DEBUG_HANDLE_LDA_DYN_V8, + &&DEBUG_HANDLE_STA_DYN_V8, + &&DEBUG_HANDLE_BUILTIN_LDBOOLEAN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_LDNUMBER_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_LDSTRING_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_LDBIGINT_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_ADD2DYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_SUB2DYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_MUL2DYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_DIV2DYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_MOD2DYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_EQDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_NOTEQDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_LESSDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_LESSEQDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_GREATERDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_GREATEREQDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_SHL2DYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_SHR2DYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_ASHR2DYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_AND2DYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_OR2DYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_XOR2DYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_TONUMBER_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_NEGDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_NOTDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_INCDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_DECDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_EXPDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_ISINDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_INSTANCEOFDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_STRICTNOTEQDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_STRICTEQDYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_RESUMEGENERATOR_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_GETRESUMEMODE_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_CREATEGENERATOROBJ_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_THROWUNDEFINED_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_THROWCONSTASSIGNMENT_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_GETTEMPLATEOBJECT_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_GETNEXTPROPNAME_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_CALLARG0DYN_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_THROWIFNOTOBJECT_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_ITERNEXT_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_CLOSEITERATOR_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_COPYMODULE_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_SUPERCALLSPREAD_IMM8_V8, + &&DEBUG_HANDLE_BUILTIN_LDOBJECT_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_LDFUNCTION_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_DELOBJPROP_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_DEFINEGLOBALVAR_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_DEFINELOCALVAR_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_DEFINEFUNCEXPR_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_REFEQDYN_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_CALLRUNTIMERANGE_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_NEWOBJSPREADDYN_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_CREATEITERRESULTOBJ_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_SUSPENDGENERATOR_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_ASYNCFUNCTIONAWAITUNCAUGHT_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_THROWUNDEFINEDIFHOLE_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_CALLARG1DYN_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_COPYDATAPROPERTIES_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_STARRAYSPREAD_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_GETITERATORNEXT_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_SETOBJECTWITHPROTO_IMM8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_CALLSPREADDYN_IMM8_V8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_ASYNCFUNCTIONRESOLVE_IMM8_V8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_ASYNCFUNCTIONREJECT_IMM8_V8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_CALLARGS2DYN_IMM8_V8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_CALLARGS3DYN_IMM8_V8_V8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_DEFINEGETTERSETTERBYVALUE_IMM8_V8_V8_V8_V8, + &&DEBUG_HANDLE_BUILTIN_TRYLDGLOBALBYVALUE_IMM8_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_NEWOBJDYNRANGE_IMM8_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_TRYSTGLOBALBYVALUE_IMM8_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_CALLIRANGEDYN_IMM8_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_CALLITHISRANGEDYN_IMM8_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_SUPERCALL_IMM8_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_LDOBJBYVALUE_IMM8_IMM16_V8_V8, + &&DEBUG_HANDLE_BUILTIN_STOBJBYVALUE_IMM8_IMM16_V8_V8, + &&DEBUG_HANDLE_BUILTIN_LDOBJBYINDEX_IMM8_IMM16_V8_V8, + &&DEBUG_HANDLE_BUILTIN_STOBJBYINDEX_IMM8_IMM16_V8_V8, + &&DEBUG_HANDLE_BUILTIN_STOWNBYINDEX_IMM8_IMM16_V8_V8, + &&DEBUG_HANDLE_BUILTIN_STOWNBYVALUE_IMM8_IMM16_V8_V8, + &&DEBUG_HANDLE_BUILTIN_CREATEOBJECTWITHEXCLUDEDKEYS_IMM8_IMM16_V8_V8, + &&DEBUG_HANDLE_BUILTIN_LDSUPERBYVALUE_IMM8_IMM16_V8_V8, + &&DEBUG_HANDLE_BUILTIN_STSUPERBYVALUE_IMM8_IMM16_V8_V8, + &&DEBUG_HANDLE_BUILTIN_IMPORTMODULE_IMM8_ID32, + &&DEBUG_HANDLE_BUILTIN_STMODULEVAR_IMM8_ID32, + &&DEBUG_HANDLE_BUILTIN_DEFINEFUNCDYN_IMM8_ID16_V8, + &&DEBUG_HANDLE_BUILTIN_DEFINENCFUNCDYN_IMM8_ID16_V8, + &&DEBUG_HANDLE_BUILTIN_DEFINEGENERATORFUNC_IMM8_ID16_V8, + &&DEBUG_HANDLE_BUILTIN_DEFINEASYNCFUNC_IMM8_ID16_V8, + &&DEBUG_HANDLE_BUILTIN_DEFINEMETHOD_IMM8_ID16_V8, + &&DEBUG_HANDLE_BUILTIN_TRYLDGLOBALBYNAME_IMM8_ID32_IMM16, + &&DEBUG_HANDLE_BUILTIN_TRYSTGLOBALBYNAME_IMM8_ID32_IMM16, + &&DEBUG_HANDLE_BUILTIN_LDGLOBALVAR_IMM8_ID32_IMM16, + &&DEBUG_HANDLE_BUILTIN_STGLOBALVAR_IMM8_ID32_IMM16, + &&DEBUG_HANDLE_BUILTIN_LDOBJBYNAME_IMM8_ID32_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_STOBJBYNAME_IMM8_ID32_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_STOWNBYNAME_IMM8_ID32_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_LDMODVARBYNAME_IMM8_ID32_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_LDSUPERBYNAME_IMM8_ID32_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_STSUPERBYNAME_IMM8_ID32_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_STLEXVARDYN_IMM8_IMM16_IMM16_V8, + &&DEBUG_HANDLE_BUILTIN_LDLEXVARDYN_IMM8_IMM16_IMM16, + &&DEBUG_HANDLE_BUILTIN_NEWLEXENVDYN_IMM8_IMM16, + &&DEBUG_HANDLE_BUILTIN_COPYRESTARGS_IMM8_IMM16, + &&DEBUG_HANDLE_BUILTIN_CREATEOBJECTWITHBUFFER_IMM8_IMM16, + &&DEBUG_HANDLE_BUILTIN_CREATEARRAYWITHBUFFER_IMM8_IMM16, + &&DEBUG_HANDLE_BUILTIN_CREATEOBJECTHAVINGMETHOD_IMM8_IMM16, + &&DEBUG_HANDLE_BUILTIN_THROWIFSUPERNOTCORRECTCALL_IMM8_IMM16, + &&DEBUG_HANDLE_BUILTIN_DEFINECLASSWITHBUFFER_IMM8_ID16_IMM16_V8_V8, + &&DEBUG_HANDLE_RETURN_DYN, + &&DEBUG_HANDLE_MOV_V4_V4, + &&DEBUG_HANDLE_JNEZ_IMM8, + &&DEBUG_HANDLE_JNEZ_IMM16, + &&DEBUG_EXCEPTION_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, + &&DEBUG_OVERFLOW_HANDLER, diff --git a/ecmascript/interpreter/templates/debugger_instruction_handler.inl b/ecmascript/interpreter/templates/debugger_instruction_handler.inl new file mode 100644 index 0000000000000000000000000000000000000000..1517b2e869a4c9f536ce7af831cb289d5c1691c4 --- /dev/null +++ b/ecmascript/interpreter/templates/debugger_instruction_handler.inl @@ -0,0 +1,785 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + HANDLE_OPCODE(DEBUG_HANDLE_MOV_DYN_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::MOV_DYN_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_MOV_DYN_V16_V16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::MOV_DYN_V16_V16); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDA_STR_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDA_STR_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDAI_DYN_IMM32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDAI_DYN_IMM32); + } + HANDLE_OPCODE(DEBUG_HANDLE_FLDAI_DYN_IMM64) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::FLDAI_DYN_IMM64); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDNAN_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDNAN_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDINFINITY_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDINFINITY_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDGLOBALTHIS_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDGLOBALTHIS_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDUNDEFINED_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDUNDEFINED_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDNULL_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDNULL_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDSYMBOL_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDSYMBOL_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDGLOBAL_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDGLOBAL_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDTRUE_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDTRUE_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDFALSE_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDFALSE_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_THROWDYN_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWDYN_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_TYPEOFDYN_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::TYPEOFDYN_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_GETUNMAPPEDARGS_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETUNMAPPEDARGS_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_TOBOOLEAN_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::TOBOOLEAN_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_GETPROPITERATOR_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETPROPITERATOR_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_ASYNCFUNCTIONENTER_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ASYNCFUNCTIONENTER_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDHOLE_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDHOLE_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_RETURNUNDEFINED_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::RETURNUNDEFINED_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CREATEEMPTYOBJECT_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEEMPTYOBJECT_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CREATEEMPTYARRAY_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEEMPTYARRAY_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_GETITERATOR_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETITERATOR_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_THROWTHROWNOTEXISTS_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWTHROWNOTEXISTS_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_THROWPATTERNNONCOERCIBLE_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWPATTERNNONCOERCIBLE_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDHOMEOBJECT_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDHOMEOBJECT_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_THROWDELETESUPERPROPERTY_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWDELETESUPERPROPERTY_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DEBUGGER_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEBUGGER_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_JMP_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JMP_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_JMP_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JMP_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_JMP_IMM32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JMP_IMM32); + } + HANDLE_OPCODE(DEBUG_HANDLE_JEQZ_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JEQZ_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_JEQZ_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JEQZ_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_JNEZ_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JNEZ_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_JNEZ_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JNEZ_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDA_DYN_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDA_DYN_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STA_DYN_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STA_DYN_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDBOOLEAN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDBOOLEAN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDNUMBER_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDNUMBER_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDSTRING_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDSTRING_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDBIGINT_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDBIGINT_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_ADD2DYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ADD2DYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_SUB2DYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SUB2DYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_MUL2DYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::MUL2DYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DIV2DYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DIV2DYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_MOD2DYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::MOD2DYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_EQDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::EQDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_NOTEQDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::NOTEQDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LESSDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LESSDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LESSEQDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LESSEQDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_GREATERDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GREATERDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_GREATEREQDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GREATEREQDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_SHL2DYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SHL2DYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_SHR2DYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SHR2DYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_ASHR2DYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ASHR2DYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_AND2DYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::AND2DYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_OR2DYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::OR2DYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_XOR2DYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::XOR2DYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_TONUMBER_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::TONUMBER_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_NEGDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::NEGDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_NOTDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::NOTDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_INCDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::INCDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DECDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DECDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_EXPDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::EXPDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_ISINDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ISINDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_INSTANCEOFDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::INSTANCEOFDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STRICTNOTEQDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STRICTNOTEQDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STRICTEQDYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STRICTEQDYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDLEXENVDYN_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDLEXENVDYN_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_POPLEXENVDYN_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::POPLEXENVDYN_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_RESUMEGENERATOR_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::RESUMEGENERATOR_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_GETRESUMEMODE_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETRESUMEMODE_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CREATEGENERATOROBJ_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEGENERATOROBJ_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_THROWUNDEFINED_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWUNDEFINED_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_THROWCONSTASSIGNMENT_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWCONSTASSIGNMENT_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_GETTEMPLATEOBJECT_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETTEMPLATEOBJECT_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_GETNEXTPROPNAME_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETNEXTPROPNAME_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CALLARG0DYN_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLARG0DYN_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_SETOBJECTWITHPROTO_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SETOBJECTWITHPROTO_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_THROWIFNOTOBJECT_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWIFNOTOBJECT_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_ITERNEXT_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ITERNEXT_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CLOSEITERATOR_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CLOSEITERATOR_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_COPYMODULE_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::COPYMODULE_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_SUPERCALLSPREAD_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SUPERCALLSPREAD_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDOBJECT_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDOBJECT_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDFUNCTION_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDFUNCTION_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DELOBJPROP_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DELOBJPROP_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DEFINEGLOBALVAR_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINEGLOBALVAR_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DEFINELOCALVAR_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINELOCALVAR_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DEFINEFUNCEXPR_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINEFUNCEXPR_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_REFEQDYN_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::REFEQDYN_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CALLRUNTIMERANGE_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLRUNTIMERANGE_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_NEWOBJSPREADDYN_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::NEWOBJSPREADDYN_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_NEWLEXENVDYN_IMM8_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::NEWLEXENVDYN_IMM8_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CREATEITERRESULTOBJ_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEITERRESULTOBJ_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_SUSPENDGENERATOR_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SUSPENDGENERATOR_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_ASYNCFUNCTIONAWAITUNCAUGHT_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ASYNCFUNCTIONAWAITUNCAUGHT_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_THROWUNDEFINEDIFHOLE_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWUNDEFINEDIFHOLE_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CALLARG1DYN_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLARG1DYN_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_COPYDATAPROPERTIES_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::COPYDATAPROPERTIES_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STARRAYSPREAD_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STARRAYSPREAD_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_GETITERATORNEXT_IMM8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETITERATORNEXT_IMM8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CALLSPREADDYN_IMM8_V8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLSPREADDYN_IMM8_V8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_ASYNCFUNCTIONRESOLVE_IMM8_V8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ASYNCFUNCTIONRESOLVE_IMM8_V8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_ASYNCFUNCTIONREJECT_IMM8_V8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ASYNCFUNCTIONREJECT_IMM8_V8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CALLARGS2DYN_IMM8_V8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLARGS2DYN_IMM8_V8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CALLARGS3DYN_IMM8_V8_V8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLARGS3DYN_IMM8_V8_V8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DEFINEGETTERSETTERBYVALUE_IMM8_V8_V8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINEGETTERSETTERBYVALUE_IMM8_V8_V8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_TRYLDGLOBALBYVALUE_IMM8_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::TRYLDGLOBALBYVALUE_IMM8_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_NEWOBJDYNRANGE_IMM8_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::NEWOBJDYNRANGE_IMM8_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_TRYSTGLOBALBYVALUE_IMM8_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::TRYSTGLOBALBYVALUE_IMM8_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CALLIRANGEDYN_IMM8_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLIRANGEDYN_IMM8_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CALLITHISRANGEDYN_IMM8_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLITHISRANGEDYN_IMM8_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_SUPERCALL_IMM8_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SUPERCALL_IMM8_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDOBJBYVALUE_IMM8_IMM16_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDOBJBYVALUE_IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STOBJBYVALUE_IMM8_IMM16_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOBJBYVALUE_IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDOBJBYINDEX_IMM8_IMM16_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDOBJBYINDEX_IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STOBJBYINDEX_IMM8_IMM16_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOBJBYINDEX_IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STOWNBYINDEX_IMM8_IMM16_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOWNBYINDEX_IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STOWNBYVALUE_IMM8_IMM16_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOWNBYVALUE_IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CREATEOBJECTWITHEXCLUDEDKEYS_IMM8_IMM16_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEOBJECTWITHEXCLUDEDKEYS_IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDSUPERBYVALUE_IMM8_IMM16_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDSUPERBYVALUE_IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STSUPERBYVALUE_IMM8_IMM16_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STSUPERBYVALUE_IMM8_IMM16_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_IMPORTMODULE_IMM8_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::IMPORTMODULE_IMM8_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STMODULEVAR_IMM8_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STMODULEVAR_IMM8_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DEFINEFUNCDYN_IMM8_ID16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINEFUNCDYN_IMM8_ID16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DEFINENCFUNCDYN_IMM8_ID16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINENCFUNCDYN_IMM8_ID16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DEFINEGENERATORFUNC_IMM8_ID16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINEGENERATORFUNC_IMM8_ID16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DEFINEASYNCFUNC_IMM8_ID16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINEASYNCFUNC_IMM8_ID16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DEFINEMETHOD_IMM8_ID16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINEMETHOD_IMM8_ID16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_TRYLDGLOBALBYNAME_IMM8_ID32_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::TRYLDGLOBALBYNAME_IMM8_ID32_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_TRYSTGLOBALBYNAME_IMM8_ID32_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::TRYSTGLOBALBYNAME_IMM8_ID32_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDGLOBALVAR_IMM8_ID32_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDGLOBALVAR_IMM8_ID32_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STGLOBALVAR_IMM8_ID32_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STGLOBALVAR_IMM8_ID32_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDOBJBYNAME_IMM8_ID32_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDOBJBYNAME_IMM8_ID32_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STOBJBYNAME_IMM8_ID32_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOBJBYNAME_IMM8_ID32_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STOWNBYNAME_IMM8_ID32_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOWNBYNAME_IMM8_ID32_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDMODVARBYNAME_IMM8_ID32_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDMODVARBYNAME_IMM8_ID32_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDSUPERBYNAME_IMM8_ID32_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDSUPERBYNAME_IMM8_ID32_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STSUPERBYNAME_IMM8_ID32_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STSUPERBYNAME_IMM8_ID32_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_LDLEXVARDYN_IMM8_IMM16_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDLEXVARDYN_IMM8_IMM16_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_STLEXVARDYN_IMM8_IMM16_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STLEXVARDYN_IMM8_IMM16_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_COPYRESTARGS_IMM8_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::COPYRESTARGS_IMM8_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CREATEOBJECTWITHBUFFER_IMM8_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM8_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CREATEARRAYWITHBUFFER_IMM8_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_CREATEOBJECTHAVINGMETHOD_IMM8_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEOBJECTHAVINGMETHOD_IMM8_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_THROWIFSUPERNOTCORRECTCALL_IMM8_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWIFSUPERNOTCORRECTCALL_IMM8_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_DEFINECLASSWITHBUFFER_IMM8_ID16_IMM16_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINECLASSWITHBUFFER_IMM8_ID16_IMM16_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_RETURN_DYN) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::RETURN_DYN); + } + HANDLE_OPCODE(DEBUG_HANDLE_MOV_V4_V4) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::MOV_V4_V4); + } + HANDLE_OPCODE(DEBUG_EXCEPTION_HANDLER) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_EXCEPTION_HANDLER(); + } + HANDLE_OPCODE(DEBUG_OVERFLOW_HANDLER) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LAST_OPCODE + 1); + } diff --git a/ecmascript/interpreter/templates/instruction_dispatch.inl b/ecmascript/interpreter/templates/instruction_dispatch.inl new file mode 100644 index 0000000000000000000000000000000000000000..6bc2f361b561002609d281b8dd9e2eec66493086 --- /dev/null +++ b/ecmascript/interpreter/templates/instruction_dispatch.inl @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + &&HANDLE_MOV_DYN_V8_V8, + &&HANDLE_MOV_DYN_V16_V16, + &&HANDLE_LDA_STR_ID32, + &&HANDLE_LDAI_DYN_IMM32, + &&HANDLE_FLDAI_DYN_IMM64, + &&HANDLE_BUILTIN_LDNAN_IMM8, + &&HANDLE_BUILTIN_LDINFINITY_IMM8, + &&HANDLE_BUILTIN_LDGLOBALTHIS_IMM8, + &&HANDLE_BUILTIN_LDUNDEFINED_IMM8, + &&HANDLE_BUILTIN_LDNULL_IMM8, + &&HANDLE_BUILTIN_LDSYMBOL_IMM8, + &&HANDLE_BUILTIN_LDGLOBAL_IMM8, + &&HANDLE_BUILTIN_LDTRUE_IMM8, + &&HANDLE_BUILTIN_LDFALSE_IMM8, + &&HANDLE_BUILTIN_THROWDYN_IMM8, + &&HANDLE_BUILTIN_TYPEOFDYN_IMM8, + &&HANDLE_BUILTIN_LDLEXENVDYN_IMM8, + &&HANDLE_BUILTIN_POPLEXENVDYN_IMM8, + &&HANDLE_BUILTIN_GETUNMAPPEDARGS_IMM8, + &&HANDLE_BUILTIN_TOBOOLEAN_IMM8, + &&HANDLE_BUILTIN_GETPROPITERATOR_IMM8, + &&HANDLE_BUILTIN_ASYNCFUNCTIONENTER_IMM8, + &&HANDLE_BUILTIN_LDHOLE_IMM8, + &&HANDLE_BUILTIN_RETURNUNDEFINED_IMM8, + &&HANDLE_BUILTIN_CREATEEMPTYOBJECT_IMM8, + &&HANDLE_BUILTIN_CREATEEMPTYARRAY_IMM8, + &&HANDLE_BUILTIN_GETITERATOR_IMM8, + &&HANDLE_BUILTIN_THROWTHROWNOTEXISTS_IMM8, + &&HANDLE_BUILTIN_THROWPATTERNNONCOERCIBLE_IMM8, + &&HANDLE_BUILTIN_LDHOMEOBJECT_IMM8, + &&HANDLE_BUILTIN_THROWDELETESUPERPROPERTY_IMM8, + &&HANDLE_BUILTIN_DEBUGGER_IMM8, + &&HANDLE_JMP_IMM8, + &&HANDLE_JMP_IMM16, + &&HANDLE_JMP_IMM32, + &&HANDLE_JEQZ_IMM8, + &&HANDLE_JEQZ_IMM16, + &&HANDLE_LDA_DYN_V8, + &&HANDLE_STA_DYN_V8, + &&HANDLE_BUILTIN_LDBOOLEAN_IMM8_V8, + &&HANDLE_BUILTIN_LDNUMBER_IMM8_V8, + &&HANDLE_BUILTIN_LDSTRING_IMM8_V8, + &&HANDLE_BUILTIN_LDBIGINT_IMM8_V8, + &&HANDLE_BUILTIN_ADD2DYN_IMM8_V8, + &&HANDLE_BUILTIN_SUB2DYN_IMM8_V8, + &&HANDLE_BUILTIN_MUL2DYN_IMM8_V8, + &&HANDLE_BUILTIN_DIV2DYN_IMM8_V8, + &&HANDLE_BUILTIN_MOD2DYN_IMM8_V8, + &&HANDLE_BUILTIN_EQDYN_IMM8_V8, + &&HANDLE_BUILTIN_NOTEQDYN_IMM8_V8, + &&HANDLE_BUILTIN_LESSDYN_IMM8_V8, + &&HANDLE_BUILTIN_LESSEQDYN_IMM8_V8, + &&HANDLE_BUILTIN_GREATERDYN_IMM8_V8, + &&HANDLE_BUILTIN_GREATEREQDYN_IMM8_V8, + &&HANDLE_BUILTIN_SHL2DYN_IMM8_V8, + &&HANDLE_BUILTIN_SHR2DYN_IMM8_V8, + &&HANDLE_BUILTIN_ASHR2DYN_IMM8_V8, + &&HANDLE_BUILTIN_AND2DYN_IMM8_V8, + &&HANDLE_BUILTIN_OR2DYN_IMM8_V8, + &&HANDLE_BUILTIN_XOR2DYN_IMM8_V8, + &&HANDLE_BUILTIN_TONUMBER_IMM8_V8, + &&HANDLE_BUILTIN_NEGDYN_IMM8_V8, + &&HANDLE_BUILTIN_NOTDYN_IMM8_V8, + &&HANDLE_BUILTIN_INCDYN_IMM8_V8, + &&HANDLE_BUILTIN_DECDYN_IMM8_V8, + &&HANDLE_BUILTIN_EXPDYN_IMM8_V8, + &&HANDLE_BUILTIN_ISINDYN_IMM8_V8, + &&HANDLE_BUILTIN_INSTANCEOFDYN_IMM8_V8, + &&HANDLE_BUILTIN_STRICTNOTEQDYN_IMM8_V8, + &&HANDLE_BUILTIN_STRICTEQDYN_IMM8_V8, + &&HANDLE_BUILTIN_RESUMEGENERATOR_IMM8_V8, + &&HANDLE_BUILTIN_GETRESUMEMODE_IMM8_V8, + &&HANDLE_BUILTIN_CREATEGENERATOROBJ_IMM8_V8, + &&HANDLE_BUILTIN_THROWUNDEFINED_IMM8_V8, + &&HANDLE_BUILTIN_THROWCONSTASSIGNMENT_IMM8_V8, + &&HANDLE_BUILTIN_GETTEMPLATEOBJECT_IMM8_V8, + &&HANDLE_BUILTIN_GETNEXTPROPNAME_IMM8_V8, + &&HANDLE_BUILTIN_CALLARG0DYN_IMM8_V8, + &&HANDLE_BUILTIN_THROWIFNOTOBJECT_IMM8_V8, + &&HANDLE_BUILTIN_ITERNEXT_IMM8_V8, + &&HANDLE_BUILTIN_CLOSEITERATOR_IMM8_V8, + &&HANDLE_BUILTIN_COPYMODULE_IMM8_V8, + &&HANDLE_BUILTIN_SUPERCALLSPREAD_IMM8_V8, + &&HANDLE_BUILTIN_LDOBJECT_IMM8_V8_V8, + &&HANDLE_BUILTIN_LDFUNCTION_IMM8_V8_V8, + &&HANDLE_BUILTIN_DELOBJPROP_IMM8_V8_V8, + &&HANDLE_BUILTIN_DEFINEGLOBALVAR_IMM8_V8_V8, + &&HANDLE_BUILTIN_DEFINELOCALVAR_IMM8_V8_V8, + &&HANDLE_BUILTIN_DEFINEFUNCEXPR_IMM8_V8_V8, + &&HANDLE_BUILTIN_REFEQDYN_IMM8_V8_V8, + &&HANDLE_BUILTIN_CALLRUNTIMERANGE_IMM8_V8_V8, + &&HANDLE_BUILTIN_NEWOBJSPREADDYN_IMM8_V8_V8, + &&HANDLE_BUILTIN_CREATEITERRESULTOBJ_IMM8_V8_V8, + &&HANDLE_BUILTIN_SUSPENDGENERATOR_IMM8_V8_V8, + &&HANDLE_BUILTIN_ASYNCFUNCTIONAWAITUNCAUGHT_IMM8_V8_V8, + &&HANDLE_BUILTIN_THROWUNDEFINEDIFHOLE_IMM8_V8_V8, + &&HANDLE_BUILTIN_CALLARG1DYN_IMM8_V8_V8, + &&HANDLE_BUILTIN_COPYDATAPROPERTIES_IMM8_V8_V8, + &&HANDLE_BUILTIN_STARRAYSPREAD_IMM8_V8_V8, + &&HANDLE_BUILTIN_GETITERATORNEXT_IMM8_V8_V8, + &&HANDLE_BUILTIN_SETOBJECTWITHPROTO_IMM8_V8_V8, + &&HANDLE_BUILTIN_CALLSPREADDYN_IMM8_V8_V8_V8, + &&HANDLE_BUILTIN_ASYNCFUNCTIONRESOLVE_IMM8_V8_V8_V8, + &&HANDLE_BUILTIN_ASYNCFUNCTIONREJECT_IMM8_V8_V8_V8, + &&HANDLE_BUILTIN_CALLARGS2DYN_IMM8_V8_V8_V8, + &&HANDLE_BUILTIN_CALLARGS3DYN_IMM8_V8_V8_V8_V8, + &&HANDLE_BUILTIN_DEFINEGETTERSETTERBYVALUE_IMM8_V8_V8_V8_V8, + &&HANDLE_BUILTIN_TRYLDGLOBALBYVALUE_IMM8_IMM16_V8, + &&HANDLE_BUILTIN_NEWOBJDYNRANGE_IMM8_IMM16_V8, + &&HANDLE_BUILTIN_TRYSTGLOBALBYVALUE_IMM8_IMM16_V8, + &&HANDLE_BUILTIN_CALLIRANGEDYN_IMM8_IMM16_V8, + &&HANDLE_BUILTIN_CALLITHISRANGEDYN_IMM8_IMM16_V8, + &&HANDLE_BUILTIN_SUPERCALL_IMM8_IMM16_V8, + &&HANDLE_BUILTIN_LDOBJBYVALUE_IMM8_IMM16_V8_V8, + &&HANDLE_BUILTIN_STOBJBYVALUE_IMM8_IMM16_V8_V8, + &&HANDLE_BUILTIN_LDOBJBYINDEX_IMM8_IMM16_V8_V8, + &&HANDLE_BUILTIN_STOBJBYINDEX_IMM8_IMM16_V8_V8, + &&HANDLE_BUILTIN_STOWNBYINDEX_IMM8_IMM16_V8_V8, + &&HANDLE_BUILTIN_STOWNBYVALUE_IMM8_IMM16_V8_V8, + &&HANDLE_BUILTIN_CREATEOBJECTWITHEXCLUDEDKEYS_IMM8_IMM16_V8_V8, + &&HANDLE_BUILTIN_STSUPERBYVALUE_IMM8_IMM16_V8_V8, + &&HANDLE_BUILTIN_LDSUPERBYVALUE_IMM8_IMM16_V8_V8, + &&HANDLE_BUILTIN_IMPORTMODULE_IMM8_ID32, + &&HANDLE_BUILTIN_STMODULEVAR_IMM8_ID32, + &&HANDLE_BUILTIN_DEFINEFUNCDYN_IMM8_ID16_V8, + &&HANDLE_BUILTIN_DEFINENCFUNCDYN_IMM8_ID16_V8, + &&HANDLE_BUILTIN_DEFINEGENERATORFUNC_IMM8_ID16_V8, + &&HANDLE_BUILTIN_DEFINEASYNCFUNC_IMM8_ID16_V8, + &&HANDLE_BUILTIN_DEFINEMETHOD_IMM8_ID16_V8, + &&HANDLE_BUILTIN_TRYLDGLOBALBYNAME_IMM8_ID32_IMM16, + &&HANDLE_BUILTIN_TRYSTGLOBALBYNAME_IMM8_ID32_IMM16, + &&HANDLE_BUILTIN_LDGLOBALVAR_IMM8_ID32_IMM16, + &&HANDLE_BUILTIN_STGLOBALVAR_IMM8_ID32_IMM16, + &&HANDLE_BUILTIN_LDOBJBYNAME_IMM8_ID32_IMM16_V8, + &&HANDLE_BUILTIN_STOBJBYNAME_IMM8_ID32_IMM16_V8, + &&HANDLE_BUILTIN_STOWNBYNAME_IMM8_ID32_IMM16_V8, + &&HANDLE_BUILTIN_LDMODVARBYNAME_IMM8_ID32_IMM16_V8, + &&HANDLE_BUILTIN_LDSUPERBYNAME_IMM8_ID32_IMM16_V8, + &&HANDLE_BUILTIN_STSUPERBYNAME_IMM8_ID32_IMM16_V8, + &&HANDLE_BUILTIN_STLEXVARDYN_IMM8_IMM16_IMM16_V8, + &&HANDLE_BUILTIN_LDLEXVARDYN_IMM8_IMM16_IMM16, + &&HANDLE_BUILTIN_NEWLEXENVDYN_IMM8_IMM16, + &&HANDLE_BUILTIN_COPYRESTARGS_IMM8_IMM16, + &&HANDLE_BUILTIN_CREATEOBJECTWITHBUFFER_IMM8_IMM16, + &&HANDLE_BUILTIN_CREATEARRAYWITHBUFFER_IMM8_IMM16, + &&HANDLE_BUILTIN_CREATEOBJECTHAVINGMETHOD_IMM8_IMM16, + &&HANDLE_BUILTIN_THROWIFSUPERNOTCORRECTCALL_IMM8_IMM16, + &&HANDLE_BUILTIN_DEFINECLASSWITHBUFFER_IMM8_ID16_IMM16_V8_V8, + &&HANDLE_RETURN_DYN, + &&HANDLE_MOV_V4_V4, + &&HANDLE_JNEZ_IMM8, + &&HANDLE_JNEZ_IMM16, + &&EXCEPTION_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, + &&OVERFLOW_HANDLER, diff --git a/ecmascript/intrinsics.cpp b/ecmascript/intrinsics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30d943cd615c27f9b3b784a643fd4d9fbec8b464 --- /dev/null +++ b/ecmascript/intrinsics.cpp @@ -0,0 +1,810 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "intrinsics.h" + +namespace panda::ecmascript::intrinsics { +DecodedTaggedValue Ldnan() +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldinfinity() +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldglobalthis() +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldundefined() +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldboolean([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldnumber([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldstring([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldbigint([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldnull() +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldsymbol() +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldobject([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldfunction([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldglobal() +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldtrue() +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ldfalse() +{ + UNREACHABLE(); +} + +DecodedTaggedValue Add2Dyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Sub2Dyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +// 12.6.3 Runtime Semantics: Evaluation +DecodedTaggedValue Mul2Dyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +// 12.6.3 Runtime Semantics: Evaluation +DecodedTaggedValue Div2Dyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +// 12.6.3 Runtime Semantics: Evaluation +DecodedTaggedValue Mod2Dyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue ExpDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue EqDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue NotEqDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue StrictEqDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue StrictNotEqDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LessDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LessEqDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue GreaterDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue GreaterEqDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LdObjByValue([[maybe_unused]] uint16_t slotId, [[maybe_unused]] int64_t objValue, + [[maybe_unused]] int64_t objTag, [[maybe_unused]] int64_t propValue, + [[maybe_unused]] int64_t propTag) +{ + UNREACHABLE(); +} + +void StObjByValue([[maybe_unused]] uint16_t slotId, [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, + [[maybe_unused]] int64_t propValue, [[maybe_unused]] int64_t propTag, + [[maybe_unused]] int64_t valValue, [[maybe_unused]] int64_t valTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue TryLdGlobalByValue([[maybe_unused]] uint16_t slotId, [[maybe_unused]] int64_t propValue, + [[maybe_unused]] int64_t propTag) +{ + UNREACHABLE(); +} + +void TryStGlobalByValue([[maybe_unused]] uint16_t slotId, [[maybe_unused]] int64_t propValue, + [[maybe_unused]] int64_t propTag, [[maybe_unused]] int64_t valValue, + [[maybe_unused]] int64_t valTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue TryLdGlobalByName([[maybe_unused]] uint32_t stringId, [[maybe_unused]] uint16_t slotId) +{ + UNREACHABLE(); +} + +void TryStGlobalByName([[maybe_unused]] uint32_t stringId, [[maybe_unused]] uint16_t slotId, + [[maybe_unused]] int64_t valValue, [[maybe_unused]] int64_t valTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LdGlobalVar([[maybe_unused]] uint32_t stringId, [[maybe_unused]] uint16_t slotId) +{ + UNREACHABLE(); +} + +void StGlobalVar([[maybe_unused]] uint32_t stringId, [[maybe_unused]] uint16_t slotId, + [[maybe_unused]] int64_t valValue, [[maybe_unused]] int64_t valTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LdObjByName([[maybe_unused]] uint32_t stringId, [[maybe_unused]] uint16_t slotId, + [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +void StObjByName([[maybe_unused]] uint32_t stringId, [[maybe_unused]] uint16_t slotId, + [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, [[maybe_unused]] int64_t valValue, + [[maybe_unused]] int64_t valTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LdObjByIndex([[maybe_unused]] uint16_t slotId, [[maybe_unused]] int64_t objValue, + [[maybe_unused]] int64_t objTag, [[maybe_unused]] int64_t idxValue, + [[maybe_unused]] int64_t idxTag) +{ + UNREACHABLE(); +} + +void StObjByIndex([[maybe_unused]] uint16_t slotId, [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, + [[maybe_unused]] int64_t idxValue, [[maybe_unused]] int64_t idxTag, [[maybe_unused]] int64_t valValue, + [[maybe_unused]] int64_t valTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue And2Dyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Or2Dyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Xor2Dyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Shl2Dyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Shr2Dyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Ashr2Dyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Tonumber([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue NegDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue NotDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue IncDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue DecDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +void ThrowDyn([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Delobjprop([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, + [[maybe_unused]] int64_t propValue, [[maybe_unused]] int64_t propTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Defineglobalvar([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, + [[maybe_unused]] int64_t a1, [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Definelocalvar([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Definefuncexpr([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + return DecodedTaggedValue(0, 0); +} + +DecodedTaggedValue DefinefuncDyn([[maybe_unused]] uint32_t methodId, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +// define not constructor function +DecodedTaggedValue DefineNCFuncDyn([[maybe_unused]] uint32_t methodId, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1, [[maybe_unused]] int64_t a2, + [[maybe_unused]] int64_t t2) +{ + UNREACHABLE(); +} + +// a0 is args' length which include ctor and new_target, and a1 is the start index of args +DecodedTaggedValue NewobjDynrange([[maybe_unused]] uint16_t numArgs, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue RefeqDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue TypeofDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Callruntimerange([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, + [[maybe_unused]] int64_t a1, [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LdLexVarDyn([[maybe_unused]] uint16_t level, [[maybe_unused]] uint16_t slot) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LdlexenvDyn() +{ + UNREACHABLE(); +} + +DecodedTaggedValue PopLexenvDyn() +{ + UNREACHABLE(); +} + +DecodedTaggedValue IsinDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue InstanceofDyn([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue NewobjspreadDyn([[maybe_unused]] int64_t funcValue, [[maybe_unused]] int64_t funcTag, + [[maybe_unused]] int64_t targetValue, [[maybe_unused]] int64_t targetTag, + [[maybe_unused]] int64_t arrValue, [[maybe_unused]] int64_t arrTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CallspreadDyn([[maybe_unused]] int64_t funcValue, [[maybe_unused]] int64_t funcTag, + [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, + [[maybe_unused]] int64_t arrValue, [[maybe_unused]] int64_t arrTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue NewlexenvDyn([[maybe_unused]] uint16_t numSlot) +{ + UNREACHABLE(); +} + +void StLexVarDyn([[maybe_unused]] uint16_t level, [[maybe_unused]] uint16_t slot, [[maybe_unused]] int64_t valueValue, + [[maybe_unused]] int64_t valueTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue GetUnmappedArgs() +{ + UNREACHABLE(); +} + +DecodedTaggedValue Toboolean([[maybe_unused]] int64_t value, [[maybe_unused]] int64_t tag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue GetPropIterator([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue DefineGeneratorFunc([[maybe_unused]] uint32_t methodId, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CreateIterResultObj([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, + [[maybe_unused]] int64_t a1, [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue SuspendGenerator([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, + [[maybe_unused]] int64_t a1, [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue ResumeGenerator([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue GetResumeMode([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CreateGeneratorObj([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +DecodedTaggedValue DefineAsyncFunc([[maybe_unused]] uint32_t methodId, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue AsyncFunctionEnter() +{ + UNREACHABLE(); +} + +DecodedTaggedValue AsyncFunctionAwaitUncaught([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, + [[maybe_unused]] int64_t a1, [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue AsyncFunctionResolve([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, + [[maybe_unused]] int64_t a1, [[maybe_unused]] int64_t t1, + [[maybe_unused]] int64_t a2, [[maybe_unused]] int64_t t2) +{ + UNREACHABLE(); +} + +DecodedTaggedValue AsyncFunctionReject([[maybe_unused]] int64_t a0, [[maybe_unused]] int64_t t0, + [[maybe_unused]] int64_t a1, [[maybe_unused]] int64_t t1, + [[maybe_unused]] int64_t a2, [[maybe_unused]] int64_t t2) +{ + UNREACHABLE(); +} + +void ThrowUndefined([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +void ThrowConstAssignment([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +void ThrowUndefinedIfHole([[maybe_unused]] int64_t accValue, [[maybe_unused]] int64_t accTag, + [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue Copyrestargs([[maybe_unused]] uint16_t index) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LdHole() +{ + UNREACHABLE(); +} + +DecodedTaggedValue GetTemplateObject([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue GetNextPropName([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +void ReturnUndefined() +{ + UNREACHABLE(); +} + +DecodedTaggedValue CallArg0Dyn([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CallArg1Dyn([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, + [[maybe_unused]] int64_t arg0Value, [[maybe_unused]] int64_t arg0Tag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CallArgs2Dyn([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, + [[maybe_unused]] int64_t arg0Value, [[maybe_unused]] int64_t arg0Tag, + [[maybe_unused]] int64_t arg1Value, [[maybe_unused]] int64_t arg1Tag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CallArgs3Dyn([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, + [[maybe_unused]] int64_t arg0Value, [[maybe_unused]] int64_t arg0Tag, + [[maybe_unused]] int64_t arg1Value, [[maybe_unused]] int64_t arg1Tag, + [[maybe_unused]] int64_t arg2Value, [[maybe_unused]] int64_t arg2Vag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CalliRangeDyn([[maybe_unused]] uint16_t num, [[maybe_unused]] int64_t objValue, + [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CalliThisRangeDyn([[maybe_unused]] uint16_t num, [[maybe_unused]] int64_t objValue, + [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CreateEmptyObject() +{ + UNREACHABLE(); +} + +DecodedTaggedValue CreateObjectWithBuffer([[maybe_unused]] uint16_t index) +{ + UNREACHABLE(); +} + +void SetObjectWithProto([[maybe_unused]] int64_t protoValue, [[maybe_unused]] int64_t protoTag, + [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CopyDataProperties([[maybe_unused]] int64_t dstValue, [[maybe_unused]] int64_t dstTag, + [[maybe_unused]] int64_t srcValue, [[maybe_unused]] int64_t srcTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue DefineGetterSetterByValue([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, + [[maybe_unused]] int64_t propValue, [[maybe_unused]] int64_t propTag, + [[maybe_unused]] int64_t getterValue, [[maybe_unused]] int64_t getterTag, + [[maybe_unused]] int64_t setterValue, [[maybe_unused]] int64_t setterTag, + [[maybe_unused]] int64_t flagValue, [[maybe_unused]] int64_t flagTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CreateEmptyArray() +{ + UNREACHABLE(); +} + +DecodedTaggedValue CreateArrayWithBuffer([[maybe_unused]] uint16_t index) +{ + UNREACHABLE(); +} + +void StOwnByIndex([[maybe_unused]] uint16_t slotId, [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, + [[maybe_unused]] int64_t idxValue, [[maybe_unused]] int64_t idxTag, [[maybe_unused]] int64_t valValue, + [[maybe_unused]] int64_t valTag) +{ + UNREACHABLE(); +} + +void StOwnByName([[maybe_unused]] uint32_t stringId, [[maybe_unused]] uint16_t slotId, + [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, [[maybe_unused]] int64_t valValue, + [[maybe_unused]] int64_t valTag) +{ + UNREACHABLE(); +} + +void StOwnByValue([[maybe_unused]] uint16_t slotId, [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, + [[maybe_unused]] int64_t propValue, [[maybe_unused]] int64_t propTag, + [[maybe_unused]] int64_t valValue, [[maybe_unused]] int64_t valTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue StArraySpread([[maybe_unused]] int64_t dstValue, [[maybe_unused]] int64_t dstTag, + [[maybe_unused]] int64_t indexValue, [[maybe_unused]] int64_t indexTag, + [[maybe_unused]] int64_t srcValue, [[maybe_unused]] int64_t srcTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue GetIterator([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +void ThrowIfNotObject([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +void ThrowThrowNotExists() +{ + UNREACHABLE(); +} + +DecodedTaggedValue CreateObjectWithExcludedKeys([[maybe_unused]] uint16_t numKeys, [[maybe_unused]] int64_t objValue, + [[maybe_unused]] int64_t objTag, [[maybe_unused]] int64_t a0, + [[maybe_unused]] int64_t t0) +{ + UNREACHABLE(); +} + +void ThrowPatternNonCoercible() +{ + UNREACHABLE(); +} + +DecodedTaggedValue IterNext([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue GetIteratorNext([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, + [[maybe_unused]] int64_t methodValue, [[maybe_unused]] int64_t methodTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CloseIterator([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue ImportModule([[maybe_unused]] uint32_t stringId) +{ + UNREACHABLE(); +} + +void StModuleVar([[maybe_unused]] uint32_t stringId, [[maybe_unused]] int64_t valValue, [[maybe_unused]] int64_t valTag) +{ + UNREACHABLE(); +} + +void CopyModule([[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LdModvarByName([[maybe_unused]] uint32_t stringId, [[maybe_unused]] uint16_t slotId, + [[maybe_unused]] int64_t objTag, [[maybe_unused]] int64_t objValue) +{ + return DecodedTaggedValue(0, 0); +} + +DecodedTaggedValue DefineClassWithBuffer([[maybe_unused]] uint32_t methodId, [[maybe_unused]] uint16_t literalIndex, + [[maybe_unused]] int64_t lexenvValue, [[maybe_unused]] int64_t lexenvTag, + [[maybe_unused]] int64_t parentValue, [[maybe_unused]] int64_t parentTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue SuperCall([[maybe_unused]] uint16_t range, [[maybe_unused]] int64_t firstVRegValue, + [[maybe_unused]] int64_t firstVRegTag, [[maybe_unused]] int64_t objValue, + [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue SuperCallSpread([[maybe_unused]] int64_t arrayValue, [[maybe_unused]] int64_t arrayTag, + [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue DefineMethod([[maybe_unused]] uint32_t methodId, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1, [[maybe_unused]] int64_t a2, [[maybe_unused]] int64_t t2) +{ + UNREACHABLE(); +} + +void StSuperByName([[maybe_unused]] uint32_t stringId, [[maybe_unused]] uint16_t slotId, + [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag, + [[maybe_unused]] int64_t valValue, [[maybe_unused]] int64_t valTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LdSuperByName([[maybe_unused]] uint32_t stringId, [[maybe_unused]] uint16_t slotId, + [[maybe_unused]] int64_t objValue, [[maybe_unused]] int64_t objTag) +{ + UNREACHABLE(); +} + +void StSuperByValue([[maybe_unused]] uint16_t slotId, [[maybe_unused]] int64_t objValue, + [[maybe_unused]] int64_t objTag, [[maybe_unused]] int64_t propValue, + [[maybe_unused]] int64_t propTag, [[maybe_unused]] int64_t valValue, + [[maybe_unused]] int64_t valTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LdSuperByValue([[maybe_unused]] uint16_t slotId, [[maybe_unused]] int64_t objValue, + [[maybe_unused]] int64_t objTag, [[maybe_unused]] int64_t propValue, + [[maybe_unused]] int64_t propTag) +{ + UNREACHABLE(); +} + +DecodedTaggedValue CreateObjectHavingMethod([[maybe_unused]] uint16_t index, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +void ThrowIfSuperNotCorrectCall([[maybe_unused]] uint16_t index, [[maybe_unused]] int64_t a1, + [[maybe_unused]] int64_t t1) +{ + UNREACHABLE(); +} + +DecodedTaggedValue LdHomeObject() +{ + UNREACHABLE(); +} + +void ThrowDeleteSuperProperty() +{ + UNREACHABLE(); +} + +void Debugger() +{ + UNREACHABLE(); +} +} // namespace panda::ecmascript::intrinsics diff --git a/ecmascript/jobs/micro_job_queue.cpp b/ecmascript/jobs/micro_job_queue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..52eee1b41e352368f43e41fe9a9be48470b2666e --- /dev/null +++ b/ecmascript/jobs/micro_job_queue.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/jobs/micro_job_queue.h" + +#include "ecmascript/global_env.h" +#include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_arguments.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_queue-inl.h" +#include "ecmascript/tagged_queue.h" +#include "utils/expected.h" + +namespace panda::ecmascript::job { +void MicroJobQueue::EnqueueJob(JSThread *thread, QueueType queueType, const JSHandle &job, + const JSHandle &argv) +{ + // 1. Assert: Type(queueName) is String and its value is the name of a Job Queue recognized by this implementation. + // 2. Assert: job is the name of a Job. + // 3. Assert: arguments is a List that has the same number of elements as the number of parameters required by job. + // 4. Let callerContext be the running execution context. + // 5. Let callerRealm be callerContext’s Realm. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle pendingJob(factory->NewPendingJob(job, argv)); + if (queueType == QueueType::QUEUE_PROMISE) { + JSHandle promiseQueue(thread, GetPromiseJobQueue()); + LOG_ECMA(DEBUG) << "promiseQueue start length: " << promiseQueue->Size(); + JSHandle newPromiseQueue( + thread, TaggedQueue::Push(thread, promiseQueue, JSHandle::Cast(pendingJob))); + SetPromiseJobQueue(thread, newPromiseQueue.GetTaggedValue()); + LOG_ECMA(DEBUG) << "promiseQueue end length: " << newPromiseQueue->Size(); + } else if (queueType == QueueType::QUEUE_SCRIPT) { + JSHandle scriptQueue(thread, GetScriptJobQueue()); + JSHandle newScriptQueue( + thread, TaggedQueue::Push(thread, scriptQueue, JSHandle::Cast(pendingJob))); + SetScriptJobQueue(thread, newScriptQueue.GetTaggedValue()); + } +} + +void MicroJobQueue::ExecutePendingJob(JSThread *thread) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle promiseQueue(thread, GetPromiseJobQueue()); + JSMutableHandle pendingJob(thread, JSTaggedValue::Undefined()); + while (!promiseQueue->Empty()) { + LOG_ECMA(DEBUG) << "promiseQueue start length: " << promiseQueue->Size(); + pendingJob.Update(promiseQueue->Pop(thread)); + LOG_ECMA(DEBUG) << "promiseQueue end length: " << promiseQueue->Size(); + PendingJob::ExecutePendingJob(pendingJob, thread); + if (thread->HasPendingException()) { + return; + } + promiseQueue = JSHandle(thread, GetPromiseJobQueue()); + } + + JSHandle scriptQueue(thread, GetScriptJobQueue()); + while (!scriptQueue->Empty()) { + pendingJob.Update(scriptQueue->Pop(thread)); + PendingJob::ExecutePendingJob(pendingJob, thread); + if (thread->HasPendingException()) { + return; + } + } +} +} // namespace panda::ecmascript::job diff --git a/ecmascript/jobs/micro_job_queue.h b/ecmascript/jobs/micro_job_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..8aa424acf0e65a813106c0a9cbfcb18a36d4436b --- /dev/null +++ b/ecmascript/jobs/micro_job_queue.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MICRO_JOB_QUEUE_H +#define PANDA_RUNTIME_ECMASCRIPT_MICRO_JOB_QUEUE_H + +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/record.h" +#include "ecmascript/tagged_array.h" + +namespace panda::ecmascript::job { +enum class QueueType : uint8_t { + QUEUE_PROMISE, + QUEUE_SCRIPT, +}; + +class MicroJobQueue final : public Record { +public: + static MicroJobQueue *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsMicroJobQueue()); + return static_cast(object); + } + + void EnqueueJob(JSThread *thread, QueueType queueType, const JSHandle &job, + const JSHandle &argv); + void ExecutePendingJob(JSThread *thread); + + static constexpr size_t PROMISE_JOB_QUEUE_OFFSET = Record::SIZE; + ACCESSORS(PromiseJobQueue, PROMISE_JOB_QUEUE_OFFSET, SCRIPT_JOB_QUEUE_OFFSET); + ACCESSORS(ScriptJobQueue, SCRIPT_JOB_QUEUE_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(PROMISE_JOB_QUEUE_OFFSET, SIZE) +}; +} // namespace panda::ecmascript::job +#endif // PANDA_RUNTIME_ECMASCRIPT_MICRO_JOB_QUEUE_H diff --git a/ecmascript/jobs/pending_job.h b/ecmascript/jobs/pending_job.h new file mode 100644 index 0000000000000000000000000000000000000000..22ca20656ec9434b9f41b3d2cda6771aa287cdbe --- /dev/null +++ b/ecmascript/jobs/pending_job.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_PENDING_JOB_H +#define PANDA_RUNTIME_ECMASCRIPT_PENDING_JOB_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_function.h" +#include "ecmascript/record.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array.h" +#include "ecmascript/mem/c_containers.h" + +namespace panda::ecmascript::job { +class PendingJob final : public Record { +public: + static PendingJob *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsPendingJob()); + return static_cast(object); + } + + static JSTaggedValue ExecutePendingJob(const JSHandle &pendingJob, JSThread *thread) + { + JSHandle job(thread, pendingJob->GetJob()); + ASSERT(job->IsCallable()); + JSHandle thisValue(thread, JSTaggedValue::Undefined()); + JSHandle argv(thread, pendingJob->GetArguments()); + return JSFunction::Call(thread, job, thisValue, argv); + } + + static constexpr size_t JOB_OFFSET = Record::SIZE; + ACCESSORS(Job, JOB_OFFSET, ARGUMENT_OFFSET); + ACCESSORS(Arguments, ARGUMENT_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(JOB_OFFSET, SIZE) +}; +} // namespace panda::ecmascript::job +#endif // PANDA_RUNTIME_ECMASCRIPT_PENDING_JOB_H diff --git a/ecmascript/js_arguments.cpp b/ecmascript/js_arguments.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6c8177d8f96ea13ea1e6906944f9ea13b2d25ff7 --- /dev/null +++ b/ecmascript/js_arguments.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_arguments.h" + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript { +bool JSArguments::GetOwnProperty(JSThread *thread, const JSHandle &args, + const JSHandle &key, PropertyDescriptor &desc) +{ + // 1 ~ 3 Let desc be OrdinaryGetOwnProperty(args, P). + JSObject::OrdinaryGetOwnProperty(thread, JSHandle(args), key, desc); + if (desc.IsEmpty()) { + return true; + } + + // 4.Let map be the value of the [[ParameterMap]] internal slot of the arguments object. + JSHandle map(thread, args->GetParameterMap()); + + // 5.Let isMapped be HasOwnProperty(map, P). + bool isMapped = JSTaggedValue::HasOwnProperty(thread, map, key); + + // 6.Assert: isMapped is never an abrupt completion. + ASSERT(!thread->HasPendingException()); + + // 7.If the value of isMapped is true, then + // a.Set desc.[[Value]] to Get(map, P). + if (isMapped) { + auto prop = JSObject::GetProperty(thread, map, key).GetValue(); + desc.SetValue(prop); + } + + // 8.If IsDataDescriptor(desc) is true and P is "caller" and desc.[[Value]] is a strict mode Function object, + // throw a TypeError exception. + JSHandle caller = thread->GetEcmaVM()->GetFactory()->NewFromString("caller"); + if (desc.IsDataDescriptor() && JSTaggedValue::SameValue(key.GetTaggedValue(), caller.GetTaggedValue()) && + desc.GetValue()->IsJSFunction()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Arguments GetOwnProperty: type error", false); + } + // 9.Return desc. + return true; +} + +bool JSArguments::DefineOwnProperty(JSThread *thread, const JSHandle &args, + const JSHandle &key, const PropertyDescriptor &desc) +{ + // 1 ~ 2 Let args be the arguments object and get map. + JSHandle map(thread, args->GetParameterMap()); + + // 3.Let isMapped be HasOwnProperty(map, P). + bool isMapped = JSTaggedValue::HasOwnProperty(thread, map, key); + + // 4.Let allowed be OrdinaryDefineOwnProperty(args, P, Desc). + bool allowed = JSObject::OrdinaryDefineOwnProperty(thread, JSHandle(args), key, desc); + + // 5.ReturnIfAbrupt(allowed). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, allowed); + + // 6.If allowed is false, return false. + if (!allowed) { + return false; + } + + // 7.If the value of isMapped is true, then + // a.If IsAccessorDescriptor(Desc) is true, then + // i.Call map.[[Delete]](P). + // b.Else + // i.If Desc.[[Value]] is present, then + // 1.Let setStatus be Set(map, P, Desc.[[Value]], false). + // 2.Assert: setStatus is true because formal parameters mapped by argument objects are always writable. + // ii.If Desc.[[Writable]] is present and its value is false, then + // 1.Call map.[[Delete]](P). + if (isMapped) { + if (desc.IsAccessorDescriptor()) { + JSTaggedValue::DeleteProperty(thread, map, key); + } else { + if (desc.HasValue()) { + [[maybe_unused]] bool setStatus = JSTaggedValue::SetProperty(thread, map, key, desc.GetValue(), false); + ASSERT(setStatus == true); + } + if (desc.HasWritable() && !desc.IsWritable()) { + JSTaggedValue::DeleteProperty(thread, map, key); + } + } + } + + // 8.Return true. + return true; +} + +OperationResult JSArguments::GetProperty(JSThread *thread, const JSHandle &args, + const JSHandle &key, const JSHandle &receiver) +{ + // 1 ~ 2 Let args be the arguments object and get map. + JSHandle map(thread, args->GetParameterMap()); + + // 3.Let isMapped be HasOwnProperty(map, P). + bool isMapped = JSTaggedValue::HasOwnProperty(thread, map, key); + + // 4.Assert: isMapped is not an abrupt completion. + ASSERT(!thread->HasPendingException()); + + // 5.If the value of isMapped is false, then + // a.Return the result of calling the default ordinary object [[Get]] internal method (9.1.8) + // on args passing P and Receiver as the arguments. + if (!isMapped) { + return JSTaggedValue::GetProperty(thread, JSHandle::Cast(args), key, receiver); + } + + // 6.Else map contains a formal parameter mapping for P, + // a.Return Get(map, P). + return JSTaggedValue::GetProperty(thread, map, key); +} + +bool JSArguments::SetProperty(JSThread *thread, const JSHandle &args, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver) +{ + // 1.Let args be the arguments object. + JSHandle map(thread, args->GetParameterMap()); + + // 2.If SameValue(args, Receiver) is false, then + // a.Let isMapped be false. + bool isMapped = false; + if (JSTaggedValue::SameValue(args.GetTaggedValue(), receiver.GetTaggedValue())) { + // 3.Else, + // a.Let map be the value of the [[ParameterMap]] internal slot of the arguments object. + // b.Let isMapped be HasOwnProperty(map, P). + // c.Assert: isMapped is not an abrupt completion. + isMapped = JSTaggedValue::HasOwnProperty(thread, map, key); + ASSERT(!thread->HasPendingException()); + } + + // 4.If isMapped is true, then + // a.Let setStatus be Set(map, P, V, false). + // b.Assert: setStatus is true because formal parameters mapped by argument objects are always writable. + if (isMapped) { + [[maybe_unused]] bool setStatus = JSTaggedValue::SetProperty(thread, map, key, value); + ASSERT(setStatus == true); + } + + // 5.Return the result of calling the default ordinary object [[Set]] internal method (9.1.9) + // on args passing P, V and Receiver as the arguments. + return JSTaggedValue::SetProperty(thread, JSHandle::Cast(args), key, value, receiver); +} + +bool JSArguments::DeleteProperty(JSThread *thread, const JSHandle &args, + const JSHandle &key) +{ + // 1.Let map be the value of the [[ParameterMap]] internal slot of the arguments object. + JSHandle map(thread, args->GetParameterMap()); + + // 2.Let isMapped be HasOwnProperty(map, P). + bool isMapped = JSTaggedValue::HasOwnProperty(thread, map, key); + + // 3.Assert: isMapped is not an abrupt completion. + ASSERT(!thread->HasPendingException()); + + // 4.Let result be the result of calling the default [[Delete]] internal method for ordinary objects (9.1.10) + // on the arguments object passing P as the argument. + bool result = JSTaggedValue::DeleteProperty(thread, JSHandle(args), key); + + // 5.ReturnIfAbrupt(result). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result); + + // 6.If result is true and the value of isMapped is true, then + // a.Call map.[[Delete]](P). + if (result && isMapped) { + JSTaggedValue::DeleteProperty(thread, map, key); + } + + // 7.Return result. + return result; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_arguments.h b/ecmascript/js_arguments.h new file mode 100644 index 0000000000000000000000000000000000000000..e4af84254fc0574f2edbaf57c4aa01ab36a8433e --- /dev/null +++ b/ecmascript/js_arguments.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSARGUMENTS_H +#define PANDA_RUNTIME_ECMASCRIPT_JSARGUMENTS_H + +#include "ecmascript/js_object.h" +#include "ecmascript/tagged_array.h" + +namespace panda::ecmascript { +class JSThread; + +class JSArguments : public JSObject { +public: + static constexpr int LENGTH_OF_INLINE_PROPERTIES = 4; + static constexpr int LENGTH_INLINE_PROPERTY_INDEX = 0; + static constexpr int ITERATOR_INLINE_PROPERTY_INDEX = 1; + static constexpr int CALLER_INLINE_PROPERTY_INDEX = 2; + static constexpr int CALLEE_INLINE_PROPERTY_INDEX = 3; + + static JSArguments *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + // 9.4.4.1 [[GetOwnProperty]] (P) + static bool GetOwnProperty(JSThread *thread, const JSHandle &args, const JSHandle &key, + PropertyDescriptor &desc); + // 9.4.4.2 [[DefineOwnProperty]] (P, Desc) + static bool DefineOwnProperty(JSThread *thread, const JSHandle &args, + const JSHandle &key, const PropertyDescriptor &desc); + // 9.4.4.3 [[Get]] (P, Receiver) + static inline OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) + { + return GetProperty(thread, JSHandle::Cast(obj), key, JSHandle::Cast(obj)); + } + static OperationResult GetProperty(JSThread *thread, const JSHandle &args, + const JSHandle &key, const JSHandle &receiver); + // 9.4.4.4 [[Set]] ( P, V, Receiver) + static inline bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) + { + return SetProperty(thread, JSHandle::Cast(obj), key, value, JSHandle::Cast(obj)); + } + static bool SetProperty(JSThread *thread, const JSHandle &args, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver); + // 9.4.4.5 [[Delete]] (P) + static bool DeleteProperty(JSThread *thread, const JSHandle &args, const JSHandle &key); + // 9.4.4.6 CreateUnmappedArgumentsObject(argumentsList) + // 9.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env ) + + // ACCESSORS(JSTaggedValue, Length, LENGTH_OFFSET) + static constexpr size_t PARAMETER_MAP_OFFSET = JSObject::SIZE; + ACCESSORS(ParameterMap, PARAMETER_MAP_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, PARAMETER_MAP_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSARGUMENTS_H diff --git a/ecmascript/js_array.cpp b/ecmascript/js_array.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d23828bb8901f7f23eaaabb07e9c87b812adbb0b --- /dev/null +++ b/ecmascript/js_array.cpp @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_array.h" +#include "ecmascript/accessor_data.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "interpreter/fast_runtime_stub-inl.h" + +namespace panda::ecmascript { +JSTaggedValue JSArray::LengthGetter([[maybe_unused]] JSThread *thread, const JSHandle &self) +{ + return JSArray::Cast(*self)->GetLength(); +} + +bool JSArray::LengthSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + bool mayThrow) +{ + uint32_t newLen = 0; + if (!JSTaggedValue::ToArrayLength(thread, value, &newLen) && mayThrow) { + THROW_RANGE_ERROR_AND_RETURN(thread, "array length must less than 2^32 - 1", false); + } + + if (!IsArrayLengthWritable(thread, self)) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property", false); + } + return false; + } + + uint32_t oldLen = JSArray::Cast(*self)->GetArrayLength(); + JSArray::SetCapacity(thread, self, oldLen, newLen); + return true; +} + +JSHandle JSArray::ArrayCreate(JSThread *thread, JSTaggedNumber length) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrayFunction = env->GetArrayFunction(); + return JSArray::ArrayCreate(thread, length, arrayFunction); +} + +// 9.4.2.2 ArrayCreate(length, proto) +JSHandle JSArray::ArrayCreate(JSThread *thread, JSTaggedNumber length, + const JSHandle &newTarget) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // Assert: length is an integer Number ≥ 0. + ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer"); + // 2. If length is −0, let length be +0. + double arrayLength = JSTaggedValue::ToInteger(thread, JSHandle(thread, length)).GetDouble(); + if (arrayLength > MAX_ARRAY_INDEX) { + JSHandle exception(thread, JSTaggedValue::Exception()); + THROW_RANGE_ERROR_AND_RETURN(thread, "array length must less than 2^32 - 1", exception); + } + uint32_t normalArrayLength = length.ToUint32(); + + // 8. Set the [[Prototype]] internal slot of A to proto. + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrayFunc = env->GetArrayFunction(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(arrayFunc), newTarget); + // 9. Set the [[Extensible]] internal slot of A to true. + obj->GetJSHClass()->SetExtensible(true); + + // 10. Perform OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]: + // true, [[Enumerable]]: false, [[Configurable]]: false}). + JSArray::Cast(*obj)->SetArrayLength(thread, normalArrayLength); + + return JSHandle(obj); +} + +// 9.4.2.3 ArraySpeciesCreate(originalArray, length) +JSTaggedValue JSArray::ArraySpeciesCreate(JSThread *thread, const JSHandle &originalArray, + JSTaggedNumber length) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // Assert: length is an integer Number ≥ 0. + ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer"); + // If length is −0, let length be +0. + double arrayLength = JSTaggedValue::ToInteger(thread, JSHandle(thread, length)).GetDouble(); + if (arrayLength == -0) { + arrayLength = +0; + } + // Let C be undefined. + // Let isArray be IsArray(originalArray). + JSHandle originalValue(originalArray); + bool isArray = originalValue->IsArray(thread); + // ReturnIfAbrupt(isArray). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // If isArray is true, then + JSHandle constructor(thread, JSTaggedValue::Undefined()); + if (isArray) { + // Let C be Get(originalArray, "constructor"). + auto *hclass = originalArray->GetJSHClass(); + if (hclass->IsJSArray() && !hclass->HasConstructor()) { + return JSArray::ArrayCreate(thread, length).GetTaggedValue(); + } + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + constructor = JSTaggedValue::GetProperty(thread, originalValue, constructorKey).GetValue(); + // ReturnIfAbrupt(C). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // If IsConstructor(C) is true, then + if (constructor->IsConstructor()) { + // Let thisRealm be the running execution context’s Realm. + // Let realmC be GetFunctionRealm(C). + JSHandle realmC = JSObject::GetFunctionRealm(thread, constructor); + // ReturnIfAbrupt(realmC). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // If thisRealm and realmC are not the same Realm Record, then + if (*realmC != *env) { + JSTaggedValue realmArrayConstructor = realmC->GetArrayFunction().GetTaggedValue(); + // If SameValue(C, realmC.[[intrinsics]].[[%Array%]]) is true, let C be undefined. + if (JSTaggedValue::SameValue(constructor.GetTaggedValue(), realmArrayConstructor)) { + return JSArray::ArrayCreate(thread, length).GetTaggedValue(); + } + } + } + + // If Type(C) is Object, then + if (constructor->IsECMAObject()) { + // Let C be Get(C, @@species). + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + constructor = JSTaggedValue::GetProperty(thread, constructor, speciesSymbol).GetValue(); + // ReturnIfAbrupt(C). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // If C is null, let C be undefined. + if (constructor->IsNull()) { + return JSArray::ArrayCreate(thread, length).GetTaggedValue(); + } + } + } + + // If C is undefined, return ArrayCreate(length). + if (constructor->IsUndefined()) { + return JSArray::ArrayCreate(thread, length).GetTaggedValue(); + } + // If IsConstructor(C) is false, throw a TypeError exception. + if (!constructor->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a constructor", JSTaggedValue::Exception()); + } + // Return Construct(C, «length»). + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1); + args->Set(thread, 0, JSTaggedValue(arrayLength)); + JSHandle newTarget(thread, JSTaggedValue::Undefined()); + JSTaggedValue result = JSFunction::Construct(thread, constructor, args, newTarget); + + // NOTEIf originalArray was created using the standard built-in Array constructor for + // a Realm that is not the Realm of the running execution context, then a new Array is + // created using the Realm of the running execution context. This maintains compatibility + // with Web browsers that have historically had that behaviour for the Array.prototype methods + // that now are defined using ArraySpeciesCreate. + return result; +} + +void JSArray::SetCapacity(JSThread *thread, const JSHandle &array, uint32_t oldLen, uint32_t newLen) +{ + TaggedArray *element = TaggedArray::Cast(array->GetElements().GetTaggedObject()); + + if (element->IsDictionaryMode()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int32_t numOfElements = array->GetNumberOfElements(); + uint32_t newNumOfElements = newLen; + if (newLen < oldLen && numOfElements != 0) { + JSHandle dictHandle(thread, element); + JSHandle newArr = factory->NewTaggedArray(numOfElements); + GetAllElementKeys(thread, array, 0, newArr); + for (uint32_t i = numOfElements - 1; i >= newLen; i--) { + JSTaggedValue value = newArr->Get(i); + uint32_t output = 0; + JSTaggedValue::StringToElementIndex(value, &output); + JSTaggedValue key(static_cast(output)); + int entry = dictHandle->FindEntry(key); + uint32_t attr = dictHandle->GetAttributes(entry).GetValue(); + PropertyAttributes propAttr(attr); + if (propAttr.IsConfigurable()) { + NumberDictionary *dictionary = NumberDictionary::Remove(thread, dictHandle, entry); + array->SetElements(thread, JSTaggedValue(dictionary)); + if (i == 0) { + newNumOfElements = i; + break; + } + } else { + newNumOfElements = i + 1; + break; + } + } + } + JSArray::Cast(*array)->SetArrayLength(thread, newNumOfElements); + return; + } + uint32_t capacity = element->GetLength(); + if (newLen <= capacity) { + // judge if need to cut down the array size, else fill the unused tail with holes + array->FillElementsWithHoles(thread, newLen, oldLen < capacity ? oldLen : capacity); + } + if (JSObject::ShouldTransToDict(oldLen, newLen)) { + JSObject::ElementsToDictionary(thread, array); + } else if (newLen > capacity) { + JSObject::GrowElementsCapacity(thread, array, newLen); + } + JSArray::Cast(*array)->SetArrayLength(thread, newLen); +} + +bool JSArray::ArraySetLength(JSThread *thread, const JSHandle &array, const PropertyDescriptor &desc) +{ + JSHandle lengthKeyHandle(thread->GlobalConstants()->GetHandledLengthString()); + + // 1. If the [[Value]] field of Desc is absent, then + if (!desc.HasValue()) { + // 1a. Return OrdinaryDefineOwnProperty(A, "length", Desc). + return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, desc); + } + // 2. Let newLenDesc be a copy of Desc. + // (Actual copying is not necessary.) + PropertyDescriptor newLenDesc = desc; + // 3. - 7. Convert Desc.[[Value]] to newLen. + uint32_t newLen = 0; + if (!JSTaggedValue::ToArrayLength(thread, desc.GetValue(), &newLen)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "array length must less than 2^32 - 1", false); + } + // 8. Set newLenDesc.[[Value]] to newLen. + // (Done below, if needed.) + // 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). + PropertyDescriptor oldLenDesc(thread); + [[maybe_unused]] bool success = GetOwnProperty(thread, array, lengthKeyHandle, oldLenDesc); + // 10. (Assert) + ASSERT(success); + + // 11. Let oldLen be oldLenDesc.[[Value]]. + uint32_t oldLen = 0; + JSTaggedValue::ToArrayLength(thread, oldLenDesc.GetValue(), &oldLen); + // 12. If newLen >= oldLen, then + if (newLen >= oldLen) { + // 8. Set newLenDesc.[[Value]] to newLen. + // 12a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc). + newLenDesc.SetValue(JSHandle(thread, JSTaggedValue(newLen))); + return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, newLenDesc); + } + // 13. If oldLenDesc.[[Writable]] is false, return false. + if (!oldLenDesc.IsWritable() || + // Also handle the {configurable: true} case since we later use + // JSArray::SetLength instead of OrdinaryDefineOwnProperty to change + // the length, and it doesn't have access to the descriptor anymore. + newLenDesc.IsConfigurable()) { + return false; + } + // 14. If newLenDesc.[[Writable]] is absent or has the value true, + // let newWritable be true. + bool newWritable = false; + if (!newLenDesc.HasWritable() || newLenDesc.IsWritable()) { + newWritable = true; + } + // 15. Else, + // 15a. Need to defer setting the [[Writable]] attribute to false in case + // any elements cannot be deleted. + // 15b. Let newWritable be false. (It's initialized as "false" anyway.) + // 15c. Set newLenDesc.[[Writable]] to true. + // (Not needed.) + + // Most of steps 16 through 19 is implemented by JSArray::SetCapacity. + JSArray::SetCapacity(thread, array, oldLen, newLen); + // Steps 19d-ii, 20. + if (!newWritable) { + PropertyDescriptor readonly(thread); + readonly.SetWritable(false); + success = JSObject::DefineOwnProperty(thread, array, lengthKeyHandle, readonly); + ASSERT_PRINT(success, "DefineOwnProperty of length must be success here!"); + } + + // Steps 19d-v, 21. Return false if there were non-deletable elements. + uint32_t arrayLength = JSArray::Cast(*array)->GetArrayLength(); + return arrayLength == newLen; +} + +bool JSArray::PropertyKeyToArrayIndex(JSThread *thread, const JSHandle &key, uint32_t *output) +{ + return JSTaggedValue::ToArrayLength(thread, key, output) && *output <= JSArray::MAX_ARRAY_INDEX; +} + +// 9.4.2.1 [[DefineOwnProperty]] ( P, Desc) +bool JSArray::DefineOwnProperty(JSThread *thread, const JSHandle &array, const JSHandle &key, + const PropertyDescriptor &desc) +{ + // 1. Assert: IsPropertyKey(P) is true. + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key!"); + // 2. If P is "length", then + if (IsLengthString(thread, key)) { + // a. Return ArraySetLength(A, Desc). + return ArraySetLength(thread, array, desc); + } + // 3. Else if P is an array index, then + // already do in step 4. + // 4. Return OrdinaryDefineOwnProperty(A, P, Desc). + bool success = JSObject::OrdinaryDefineOwnProperty(thread, array, key, desc); + if (success) { + JSTaggedValue constructorKey = thread->GlobalConstants()->GetConstructorString(); + if (key.GetTaggedValue() == constructorKey) { + array->GetJSHClass()->SetHasConstructor(true); + return true; + } + } + return success; +} + +bool JSArray::DefineOwnProperty(JSThread *thread, const JSHandle &array, uint32_t index, + const PropertyDescriptor &desc) +{ + return JSObject::OrdinaryDefineOwnProperty(thread, array, index, desc); +} + +bool JSArray::IsLengthString(JSThread *thread, const JSHandle &key) +{ + return key.GetTaggedValue() == thread->GlobalConstants()->GetLengthString(); +} + +// ecma6 7.3 Operations on Objects +JSHandle JSArray::CreateArrayFromList(JSThread *thread, const JSHandle &elements) +{ + // Assert: elements is a List whose elements are all ECMAScript language values. + // 2. Let array be ArrayCreate(0) (see 9.4.2.2). + uint32_t length = elements->GetLength(); + + // 4. For each element e of elements + auto env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle arrayFunc = env->GetArrayFunction(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(arrayFunc), arrayFunc); + obj->GetJSHClass()->SetExtensible(true); + JSArray::Cast(*obj)->SetArrayLength(thread, length); + + obj->SetElements(thread, elements); + + return JSHandle(obj); +} + +JSHandle JSArray::FastGetPropertyByValue(JSThread *thread, const JSHandle &obj, + uint32_t index) +{ + auto result = FastRuntimeStub::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), index); + return JSHandle(thread, result); +} + +JSHandle JSArray::FastGetPropertyByValue(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + auto result = FastRuntimeStub::FastGetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue()); + return JSHandle(thread, result); +} + +bool JSArray::FastSetPropertyByValue(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value) +{ + return FastRuntimeStub::FastSetPropertyByIndex(thread, obj.GetTaggedValue(), index, value.GetTaggedValue()); +} + +bool JSArray::FastSetPropertyByValue(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value) +{ + return FastRuntimeStub::FastSetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(), + value.GetTaggedValue()); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_array.h b/ecmascript/js_array.h new file mode 100644 index 0000000000000000000000000000000000000000..d311ae6498ea5fc9ac4ee20a610423f9185976ce --- /dev/null +++ b/ecmascript/js_array.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_JSARRAY_H + +#include +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/tagged_array.h" + +namespace panda::ecmascript { +// ecma6 9.4.2 Array Exotic Object +class JSArray : public JSObject { +public: + static constexpr int LENGTH_INLINE_PROPERTY_INDEX = 0; + + static JSArray *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSArray()); + return static_cast(object); + } + + static JSHandle ArrayCreate(JSThread *thread, JSTaggedNumber length); + static JSHandle ArrayCreate(JSThread *thread, JSTaggedNumber length, + const JSHandle &newTarget); + static JSTaggedValue ArraySpeciesCreate(JSThread *thread, const JSHandle &originalArray, + JSTaggedNumber length); + static bool ArraySetLength(JSThread *thread, const JSHandle &array, const PropertyDescriptor &desc); + static bool DefineOwnProperty(JSThread *thread, const JSHandle &array, const JSHandle &key, + const PropertyDescriptor &desc); + static bool DefineOwnProperty(JSThread *thread, const JSHandle &array, uint32_t index, + const PropertyDescriptor &desc); + + static bool IsLengthString(JSThread *thread, const JSHandle &key); + // ecma6 7.3 Operations on Objects + static JSHandle CreateArrayFromList(JSThread *thread, const JSHandle &elements); + // use first inlined property slot for array length + inline uint32_t GetArrayLength() const + { + return GetLength().GetArrayLength(); + } + + inline void SetArrayLength(const JSThread *thread, uint32_t length) + { + SetLength(thread, JSTaggedValue(length)); + } + + static constexpr size_t LENGTH_OFFSET = JSObject::SIZE; + ACCESSORS(Length, LENGTH_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LENGTH_OFFSET, SIZE) + + static const uint32_t MAX_ARRAY_INDEX = MAX_ELEMENT_INDEX; + DECL_DUMP() + + static int32_t GetArrayLengthOffset() + { + return LENGTH_OFFSET; + } + + static bool PropertyKeyToArrayIndex(JSThread *thread, const JSHandle &key, uint32_t *output); + + static JSTaggedValue LengthGetter(JSThread *thread, const JSHandle &self); + + static bool LengthSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + bool mayThrow = false); + + static JSHandle FastGetPropertyByValue(JSThread *thread, const JSHandle &obj, + uint32_t index); + + static JSHandle FastGetPropertyByValue(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static bool FastSetPropertyByValue(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value); + + static bool FastSetPropertyByValue(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value); + +private: + static void SetCapacity(JSThread *thread, const JSHandle &array, uint32_t oldLen, uint32_t newLen); +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSARRAY_H diff --git a/ecmascript/js_array_iterator.cpp b/ecmascript/js_array_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ded4229d8214d3c24b337a4c105ed45b1d9a78a --- /dev/null +++ b/ecmascript/js_array_iterator.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_array_iterator.h" +#include "builtins/builtins_errors.h" +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/base/typed_array_helper.h" +#include "global_env.h" +#include "js_array.h" +#include "object_factory.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +// 22.1.5.2.1 %ArrayIteratorPrototype%.next ( ) +JSTaggedValue JSArrayIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.Let O be the this value. + JSHandle input(BuiltinsBase::GetThis(argv)); + + // 2.If Type(O) is not Object, throw a TypeError exception. + // 3.If O does not have all of the internal slots of an TaggedArray Iterator Instance (22.1.5.3), throw a TypeError + // exception. + if (!input->IsJSArrayIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an array iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + // 4.Let a be O.[[IteratedArrayLike]]. + JSHandle array(thread, iter->GetIteratedArray()); + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + // 5.If a is undefined, return CreateIterResultObject(undefined, true). + if (array->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + // 6.Let index be O.[[ArrayLikeNextIndex]]. + uint32_t index = iter->GetNextIndex().GetInt(); + // 7.Let itemKind be O.[[ArrayLikeIterationKind]]. + IterationKind itemKind = IterationKind(iter->GetIterationKind().GetInt()); + + uint32_t length; + // 8. If a has a [[TypedArrayName]] internal slot, then + // a. Let len be the value of O’s [[ArrayLength]] internal slot. + if (array->IsTypedArray()) { + length = base::TypedArrayHelper::GetArrayLength(thread, JSHandle(array)); + } else { + // 9.Else + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + length = JSTaggedValue::GetProperty(thread, array, lengthKey).GetValue()->GetArrayLength(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + // 10.If index ≥ len, then + if (index >= length) { + // Set O.[[IteratedArrayLike]] to undefined. + // Return CreateIterResultObject(undefined, true). + iter->SetIteratedArray(thread, undefinedHandle); + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + // 11.Set O.[[ArrayLikeNextIndex]] to index + 1. + iter->SetNextIndex(thread, JSTaggedValue(index + 1)); + // 12.If itemKind is key, return CreateIterResultObject(index, false). + JSHandle key(thread, JSTaggedValue(index)); + if (itemKind == IterationKind::KEY) { + return JSIterator::CreateIterResultObject(thread, key, false).GetTaggedValue(); + } + JSHandle sKey(JSTaggedValue::ToString(thread, key)); + JSHandle value = JSTaggedValue::GetProperty(thread, array, sKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 15.If itemKind is value, let result be elementValue. + if (itemKind == IterationKind::VALUE) { + return JSIterator::CreateIterResultObject(thread, value, false).GetTaggedValue(); + } + // 16. Else + ASSERT_PRINT(itemKind == IterationKind::KEY_AND_VALUE, "itemKind is invalid"); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle resultArray(factory->NewTaggedArray(2)); // 2 means the length of array + resultArray->Set(thread, 0, key); + resultArray->Set(thread, 1, value); + JSHandle keyAndValue(JSArray::CreateArrayFromList(thread, resultArray)); + return JSIterator::CreateIterResultObject(thread, keyAndValue, false).GetTaggedValue(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_array_iterator.h b/ecmascript/js_array_iterator.h new file mode 100644 index 0000000000000000000000000000000000000000..c604aff2fb0a0bd9308a76220dfbbca6a1fc53da --- /dev/null +++ b/ecmascript/js_array_iterator.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_ARRAY_ITERATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_ARRAY_ITERATOR_H + +#include "js_iterator.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSArrayIterator : public JSObject { +public: + static JSArrayIterator *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsJSArrayIterator()); + return static_cast(obj); + } + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + + static constexpr size_t ITERATED_ARRAY_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedArray, ITERATED_ARRAY_OFFSET, NEXT_INDEX_OFFSET) + ACCESSORS(NextIndex, NEXT_INDEX_OFFSET, ITERATION_KIND_OFFSET) + ACCESSORS(IterationKind, ITERATION_KIND_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_ARRAY_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_ARRAY_ITERATOR_H diff --git a/ecmascript/js_arraybuffer.cpp b/ecmascript/js_arraybuffer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..af3dd367acdb9bda1ef7f4128dcf6bbc4d67583f --- /dev/null +++ b/ecmascript/js_arraybuffer.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_arraybuffer.h" + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array.h" +#include "securec.h" + +namespace panda::ecmascript { +void JSArrayBuffer::CopyDataBlockBytes(JSTaggedValue toBlock, JSTaggedValue fromBlock, int32_t fromIndex, int32_t count) +{ + void *fromBuf = JSNativePointer::Cast(fromBlock.GetTaggedObject())->GetExternalPointer(); + void *toBuf = JSNativePointer::Cast(toBlock.GetTaggedObject())->GetExternalPointer(); + auto *from = static_cast(fromBuf); + auto *to = static_cast(toBuf); + if (memcpy_s(to, count, from + fromIndex, count) != EOK) { // NOLINT + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } +} + +void JSArrayBuffer::Attach(JSThread *thread, JSTaggedValue arrayBufferByteLength, JSTaggedValue arrayBufferData) +{ + ASSERT(arrayBufferData.IsNativePointer()); + SetArrayBufferByteLength(thread, arrayBufferByteLength); + SetArrayBufferData(thread, arrayBufferData); + EcmaVM *vm = thread->GetEcmaVM(); + vm->PushToArrayDataList(JSNativePointer::Cast(arrayBufferData.GetHeapObject())); +} + +void JSArrayBuffer::Detach(JSThread *thread) +{ + JSTaggedValue arrayBufferData = GetArrayBufferData(); + // already detached. + if (arrayBufferData.IsNull()) { + return; + } + + EcmaVM *vm = thread->GetEcmaVM(); + // remove vm's control over arrayBufferData. + JSNativePointer *jsNativePointer = JSNativePointer::Cast(arrayBufferData.GetHeapObject()); + vm->RemoveArrayDataList(jsNativePointer); + jsNativePointer->Destroy(); + + SetArrayBufferData(thread, JSTaggedValue::Null()); + SetArrayBufferByteLength(thread, JSTaggedValue(0)); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_arraybuffer.h b/ecmascript/js_arraybuffer.h new file mode 100644 index 0000000000000000000000000000000000000000..0be53df9ef3270712cfa2b0beecd105833f2709d --- /dev/null +++ b/ecmascript/js_arraybuffer.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSARRAYBUFFER_H +#define PANDA_RUNTIME_ECMASCRIPT_JSARRAYBUFFER_H + +#include "ecmascript/js_object.h" + +namespace panda::ecmascript { +class JSArrayBuffer final : public JSObject { +public: + static JSArrayBuffer *Cast(ObjectHeader *object) + { + return static_cast(object); + } + // 6.2.6.2 + static void CopyDataBlockBytes(JSTaggedValue toBlock, JSTaggedValue fromBlock, int32_t fromIndex, int32_t count); + + void Attach(JSThread *thread, JSTaggedValue arrayBufferByteLength, JSTaggedValue arrayBufferData); + void Detach(JSThread *thread); + + bool IsDetach() + { + JSTaggedValue arrayBufferData = GetArrayBufferData(); + return arrayBufferData == JSTaggedValue::Null(); + } + + static constexpr size_t ARRAY_BUFFER_BYTE_LENGTH_OFFSET = JSObject::SIZE; + ACCESSORS(ArrayBufferByteLength, ARRAY_BUFFER_BYTE_LENGTH_OFFSET, ARRAY_BUFFER_DATA_OFFSET) + ACCESSORS(ArrayBufferData, ARRAY_BUFFER_DATA_OFFSET, ARRAY_BUFFER_SHARED_OFFSET) + ACCESSORS(Shared, ARRAY_BUFFER_SHARED_OFFSET, SIZE) + + DECL_DUMP() + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ARRAY_BUFFER_BYTE_LENGTH_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSARRAYBUFFER_H \ No newline at end of file diff --git a/ecmascript/js_async_function.cpp b/ecmascript/js_async_function.cpp new file mode 100644 index 0000000000000000000000000000000000000000..94a734def059c4aae74766e5e9a4ce8f22aaf9c1 --- /dev/null +++ b/ecmascript/js_async_function.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_async_function.h" + +#include "ecmascript/builtins/builtins_promise.h" +#include "ecmascript/builtins/builtins_promise_handler.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/generator_helper.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +using BuiltinsPromiseHandler = builtins::BuiltinsPromiseHandler; +using BuiltinsPromise = builtins::BuiltinsPromise; + +void JSAsyncFunction::AsyncFunctionAwait(JSThread *thread, const JSHandle &asyncFuncObj, + const JSHandle &value) +{ + // 1.Let asyncContext be the running execution context. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle asyncCtxt(thread, asyncFuncObj->GetGeneratorContext()); + + // 2.Let promiseCapability be ! NewPromiseCapability(%Promise%). + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle pcap = + JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + + // 3.Let resolveResult be ! Call(promiseCapability.[[Resolve]], undefined, « value »). + JSHandle resolve(thread, pcap->GetResolve()); + JSHandle thisArg = globalConst->GetHandledUndefined(); + JSHandle args = factory->NewTaggedArray(1); + args->Set(thread, 0, value); + [[maybe_unused]] JSTaggedValue res = JSFunction::Call(thread, resolve, thisArg, args); + + // 4.Let onFulfilled be a new built-in function object as defined in AsyncFunction Awaited Fulfilled. + JSHandle fulFunc = + factory->NewJSAsyncAwaitStatusFunction(reinterpret_cast(BuiltinsPromiseHandler::AsyncAwaitFulfilled)); + + // 5.Let onRejected be a new built-in function object as defined in AsyncFunction Awaited Rejected. + JSHandle rejFunc = + factory->NewJSAsyncAwaitStatusFunction(reinterpret_cast(BuiltinsPromiseHandler::AsyncAwaitRejected)); + + // 6.Set onFulfilled.[[AsyncContext]] to asyncContext. + // 7.Set onRejected.[[AsyncContext]] to asyncContext. + fulFunc->SetAsyncContext(thread, asyncCtxt); + rejFunc->SetAsyncContext(thread, asyncCtxt); + + // 8.Let throwawayCapability be ! NewPromiseCapability(%Promise%). + // 9.Set throwawayCapability.[[Promise]].[[PromiseIsHandled]] to true. + JSHandle tcap = + JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + JSHandle(thread, tcap->GetPromise())->SetPromiseIsHandled(thread, JSTaggedValue::Undefined()); + + // 10.Perform ! PerformPromiseThen(promiseCapability.[[Promise]], onFulfilled, onRejected, throwawayCapability). + JSHandle promise(thread, pcap->GetPromise()); + [[maybe_unused]] JSTaggedValue pres = BuiltinsPromise::PerformPromiseThen( + thread, promise, JSHandle::Cast(fulFunc), JSHandle::Cast(rejFunc), tcap); + + // 11.Remove asyncContext from the execution context stack and restore the execution context that + // is at the top of the execution context stack as the running execution context. + // 12.Set the code evaluation state of asyncContext such that when evaluation is resumed with a Completion + // resumptionValue the following steps will be performed: + // a.Return resumptionValue. + // 13.Return. +} + +JSHandle JSAsyncAwaitStatusFunction::AsyncFunctionAwaitFulfilled( + JSThread *thread, const JSHandle &func, const JSHandle &value) +{ + // 1.Let asyncContext be F.[[AsyncContext]]. + JSHandle asyncCtxt(thread, func->GetAsyncContext()); + + // 2.Let prevContext be the running execution context. + C2IBridge c2i; + GeneratorHelper::ChangeGenContext(thread, asyncCtxt, &c2i); + // 3.Suspend prevContext. + // 4.Push asyncContext onto the execution context stack; asyncContext is now the running execution context. + // 5.Resume the suspended evaluation of asyncContext using NormalCompletion(value) as the result of the + // operation that suspended it. Let result be the value returned by the resumed computation. + JSHandle result = GeneratorHelper::Next(thread, asyncCtxt, value.GetTaggedValue()); + GeneratorHelper::ResumeContext(thread); + // 6.Assert: When we reach this step, asyncContext has already been removed from the execution context stack + // and prevContext is the currently running execution context. + + // 7.Return Completion(result). + return JSHandle::Cast(result); +} + +JSHandle JSAsyncAwaitStatusFunction::AsyncFunctionAwaitRejected( + JSThread *thread, const JSHandle &func, const JSHandle &reason) +{ + // 1.Let asyncContext be F.[[AsyncContext]]. + JSHandle asyncCtxt(thread, func->GetAsyncContext()); + + // 2.Let prevContext be the running execution context. + C2IBridge c2i; + GeneratorHelper::ChangeGenContext(thread, asyncCtxt, &c2i); + // 3.Suspend prevContext. + // 4.Push asyncContext onto the execution context stack; asyncContext is now the running execution context. + // 5.Resume the suspended evaluation of asyncContext using Completion{[[Type]]: throw, + // [[Value]]: reason, [[Target]]: empty} as the result of the operation that suspended it. + // Let result be the value returned by the resumed computation. + JSHandle result = GeneratorHelper::Throw(thread, asyncCtxt, reason.GetTaggedValue()); + GeneratorHelper::ResumeContext(thread); + // 6.Assert: When we reach this step, asyncContext has already been removed from the execution context stack + // and prevContext is the currently running execution context. + + // 7.Return Completion(result). + return JSHandle::Cast(result); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_async_function.h b/ecmascript/js_async_function.h new file mode 100644 index 0000000000000000000000000000000000000000..92c59568b9b30353bd1a4bd5f1e0073da62b9627 --- /dev/null +++ b/ecmascript/js_async_function.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_ASYNC_FUNCTION_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_ASYNC_FUNCTION_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_tagged_value.h" +#include "js_function.h" + +namespace panda::ecmascript { +class JSAsyncAwaitStatusFunction : public JSFunction { +public: + static JSAsyncAwaitStatusFunction *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSAsyncAwaitStatusFunction()); + return static_cast(object); + } + + static JSHandle AsyncFunctionAwaitFulfilled(JSThread *thread, + const JSHandle &func, + const JSHandle &value); + + static JSHandle AsyncFunctionAwaitRejected(JSThread *thread, + const JSHandle &func, + const JSHandle &reason); + + static constexpr size_t ASYNC_CONTEXT_OFFSET = JSFunction::SIZE; + ACCESSORS(AsyncContext, ASYNC_CONTEXT_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, ASYNC_CONTEXT_OFFSET, SIZE) + + DECL_DUMP() +}; + +class JSAsyncFunction : public JSFunction { +public: + static JSAsyncFunction *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSAsyncFunction()); + return static_cast(object); + } + + static void AsyncFunctionAwait(JSThread *thread, const JSHandle &asyncFuncObj, + const JSHandle &value); + static constexpr size_t SIZE = JSFunction::SIZE; + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, SIZE, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_ASYNC_FUNCTION_H diff --git a/ecmascript/js_dataview.cpp b/ecmascript/js_dataview.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8636a33b624c60b8074d8902ff596731a639bdcc --- /dev/null +++ b/ecmascript/js_dataview.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_dataview.h" + +namespace panda::ecmascript { +int32_t JSDataView::GetElementSize(DataViewType type) +{ + int32_t size; + switch (type) { + case DataViewType::INT8: + case DataViewType::UINT8: + case DataViewType::UINT8_CLAMPED: + size = 1; + break; + case DataViewType::INT16: + case DataViewType::UINT16: + size = 2; // 2 means the length + break; + case DataViewType::INT32: + case DataViewType::UINT32: + case DataViewType::FLOAT32: + size = 4; // 4 means the length + break; + case DataViewType::FLOAT64: + size = 8; // 8 means the length + break; + default: + UNREACHABLE(); + } + return size; +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/js_dataview.h b/ecmascript/js_dataview.h new file mode 100644 index 0000000000000000000000000000000000000000..3d563f63985887e18942bd6ee46c824fda2f8517 --- /dev/null +++ b/ecmascript/js_dataview.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSDATAVIEW_H +#define PANDA_RUNTIME_ECMASCRIPT_JSDATAVIEW_H + +#include "ecmascript/js_object.h" + +namespace panda::ecmascript { +enum class DataViewType : uint8_t { FLOAT32 = 0, FLOAT64, INT8, INT16, INT32, UINT8, UINT16, UINT32, UINT8_CLAMPED }; +class JSDataView : public JSObject { +public: + static JSDataView *Cast(ObjectHeader *object) + { + return static_cast(object); + } + static int32_t GetElementSize(DataViewType type); + + static constexpr size_t DATA_VIEW_OFFSET = JSObject::SIZE; + ACCESSORS(DataView, DATA_VIEW_OFFSET, VIEW_ARRAY_BUFFER_OFFSET); + ACCESSORS(ViewedArrayBuffer, VIEW_ARRAY_BUFFER_OFFSET, BYTE_LENGTH_OFFSET); + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET); + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, DATA_VIEW_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSDATAVIEW_H diff --git a/ecmascript/js_date.cpp b/ecmascript/js_date.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ececd323c3537ec3a2733006a051286fd3b9a30f --- /dev/null +++ b/ecmascript/js_date.cpp @@ -0,0 +1,1001 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_date.h" +#include +#include +#include +#include "base/builtins_base.h" + +namespace panda::ecmascript { +using NumberHelper = base::NumberHelper; +void DateUtils::TransferTimeToDate(int64_t timeMs, std::array *date) +{ + (*date)[HOUR] = Mod(timeMs, MS_PER_DAY); // ms from hour, minutes, second, ms + (*date)[DAYS] = (timeMs - (*date)[HOUR]) / MS_PER_DAY; // days from year, month, day + (*date)[MS] = (*date)[HOUR] % MS_PER_SECOND; // ms + (*date)[HOUR] = ((*date)[HOUR] - (*date)[MS]) / MS_PER_SECOND; // s from hour, minutes, second + (*date)[SEC] = (*date)[HOUR] % SEC_PER_MINUTE; // second + (*date)[HOUR] = ((*date)[HOUR] - (*date)[SEC]) / SEC_PER_MINUTE; // min from hour, minutes + (*date)[MIN] = (*date)[HOUR] % SEC_PER_MINUTE; // min + (*date)[HOUR] = ((*date)[HOUR] - (*date)[MIN]) / SEC_PER_MINUTE; // hour + (*date)[WEEKDAY] = Mod(((*date)[DAYS] + LEAP_NUMBER[0]), DAY_PER_WEEK); // weekday + (*date)[YEAR] = GetYearFromDays(&((*date)[DAYS])); // year +} +// static +bool DateUtils::IsLeap(int64_t year) +{ + return year % LEAP_NUMBER[0] == 0 && (year % LEAP_NUMBER[1] != 0 || year % LEAP_NUMBER[2] == 0); // 2: means index +} + +// static +int64_t DateUtils::GetDaysInYear(int64_t year) +{ + int64_t number; + number = IsLeap(year) ? (DAYS_IN_YEAR + 1) : DAYS_IN_YEAR; + return number; +} + +// static +int64_t DateUtils::GetDaysFromYear(int64_t year) +{ + return DAYS_IN_YEAR * (year - YEAR_NUMBER[0]) + FloorDiv(year - YEAR_NUMBER[1], LEAP_NUMBER[0]) - + FloorDiv(year - YEAR_NUMBER[2], LEAP_NUMBER[1]) + // 2: year index + FloorDiv(year - YEAR_NUMBER[3], LEAP_NUMBER[2]); // 3, 2: year index +} + +// static +int64_t DateUtils::FloorDiv(int64_t a, int64_t b) +{ + ASSERT(b != 0); + int64_t m = a % b; + int64_t res = m < 0 ? ((a - m - b) / b) : ((a - m) / b); + return res; +} + +// static +int64_t DateUtils::GetYearFromDays(int64_t *days) +{ + int64_t realDay; + int64_t dayTemp = 0; + int64_t d = *days; + int64_t year = FloorDiv(d * APPROXIMATION_NUMBER[0], APPROXIMATION_NUMBER[1]) + YEAR_NUMBER[0]; + realDay = d - GetDaysFromYear(year); + while (realDay != 0) { + if (realDay < 0) { + year--; + } else { + dayTemp = GetDaysInYear(year); + if (realDay < dayTemp) { + break; + } + year++; + } + realDay = d - GetDaysFromYear(year); + } + *days = realDay; + return year; +} + +// static +int64_t DateUtils::Mod(int64_t a, int b) +{ + ASSERT(b != 0); + int64_t m = a % b; + int64_t res = m < 0 ? (m + b) : m; + return res; +} + +// static +// 20.4.1.11 +double JSDate::MakeTime(double hour, double min, double sec, double ms) +{ + if (std::isfinite(hour) && std::isfinite(min) && std::isfinite(sec) && std::isfinite(ms)) { + double hourInteger = NumberHelper::TruncateDouble(hour); + double minInteger = NumberHelper::TruncateDouble(min); + double secInteger = NumberHelper::TruncateDouble(sec); + double msInteger = NumberHelper::TruncateDouble(ms); + return hourInteger * MS_PER_HOUR + minInteger * MS_PER_MINUTE + secInteger * MS_PER_SECOND + msInteger; + } + return base::NAN_VALUE; +} + +// static +// 20.4.1.12 +double JSDate::MakeDay(double year, double month, double date) +{ + if (std::isfinite(year) && std::isfinite(month) && std::isfinite(date)) { + double yearInteger = NumberHelper::TruncateDouble(year); + double monthInteger = NumberHelper::TruncateDouble(month); + int64_t y = static_cast(yearInteger) + static_cast(monthInteger / MOUTH_PER_YEAR); + int64_t m = static_cast(monthInteger) % MOUTH_PER_YEAR; + if (m < 0) { + m += MOUTH_PER_YEAR; + y -= 1; + } + + int64_t days = DateUtils::GetDaysFromYear(y); + int index = DateUtils::IsLeap(year) ? 1 : 0; + days += DAYS_FROM_MONTH[index][m]; + return static_cast(days - 1) + NumberHelper::TruncateDouble(date); + } + return base::NAN_VALUE; +} + +// static +// 20.4.1.13 +double JSDate::MakeDate(double day, double time) +{ + if (std::isfinite(day) && std::isfinite(time)) { + return time + day * MS_PER_DAY; + } + return base::NAN_VALUE; +} + +// static +// 20.4.1.14 +double JSDate::TimeClip(double time) +{ + if (-MAX_TIME_IN_MS <= time && time <= MAX_TIME_IN_MS) { + return NumberHelper::TruncateDouble(time); + } + return base::NAN_VALUE; +} + +// 20.4.1.8 +double JSDate::LocalTime(double timeMs) const +{ + return timeMs + GetLocalOffsetFromOS(timeMs, true); +} + +// 20.4.1.9 +double JSDate::UTCTime(double timeMs) const +{ + return timeMs - GetLocalOffsetFromOS(timeMs, false); +} + +// static +int JSDate::GetSignedNumFromString(const CString &str, int len, int *index) +{ + int res = 0; + GetNumFromString(str, len, index, &res); + if (str.at(0) == NEG) { + return -res; + } + return res; +} + +// static +bool JSDate::GetNumFromString(const CString &str, int len, int *index, int *num) +{ + int indexStr = *index; + char oneByte = 0; + while (indexStr < len) { + oneByte = str.at(indexStr); + if (oneByte >= '0' && oneByte <= '9') { + break; + } + indexStr++; + } + if (indexStr >= len) { + return false; + } + int value = 0; + while (indexStr < len) { + oneByte = str.at(indexStr); + int val = oneByte - '0'; + if (val >= 0 && val <= NUM_NINE) { + value = value * TEN + val; + indexStr++; + } else { + break; + } + } + *num = value; + *index = indexStr; + return true; +} + +// 20.4.1.7 +int64_t JSDate::GetLocalOffsetInMin(const JSThread *thread, int64_t timeMs, bool isLocal) +{ + if (!isLocal) { + return 0; + } + double localOffset = this->GetLocalOffset().GetDouble(); + if (localOffset == MAX_DOUBLE) { + localOffset = static_cast(GetLocalOffsetFromOS(timeMs, isLocal)); + SetLocalOffset(thread, JSTaggedValue(localOffset)); + } + return localOffset; +} + +// static +JSTaggedValue JSDate::LocalParseStringToMs(const CString &str) +{ + int year = 0; + int month = 0; + int date = 1; + int hours = 0; + int minutes = 0; + int seconds = 0; + int ms = 0; + int index = 0; + int len = str.length(); + bool isLocal = false; + CString::size_type indexGmt; + CString::size_type indexPlus = CString::npos; + std::array monthName = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + int localTime = 0; + int localHours = 0; + int localMinutes = 0; + int64_t localMs = 0; + CString::size_type localSpace; + localSpace = str.find(' ', index); + CString strMonth = str.substr(localSpace + 1, LENGTH_MONTH_NAME); + for (int i = 0; i < MOUTH_PER_YEAR; i++) { + if (strMonth == monthName[i]) { + month = i; + break; + } + } + index += (LENGTH_MONTH_NAME + 1); + GetNumFromString(str, len, &index, &date); + GetNumFromString(str, len, &index, &year); + indexGmt = str.find("GMT", index); + if (indexGmt == CString::npos) { + GetNumFromString(str, len, &index, &hours); + GetNumFromString(str, len, &index, &minutes); + GetNumFromString(str, len, &index, &seconds); + isLocal = true; + localMs -= (GetLocalOffsetFromOS(localMs, true) * MS_PER_MINUTE); + } else { + indexPlus = str.find(PLUS, indexGmt); + int indexLocal = static_cast(indexGmt); + GetNumFromString(str, indexGmt, &index, &hours); + GetNumFromString(str, indexGmt, &index, &minutes); + GetNumFromString(str, indexGmt, &index, &seconds); + GetNumFromString(str, len, &indexLocal, &localTime); + localHours = localTime / HUNDRED; + localMinutes = localTime % HUNDRED; + localMs = static_cast(MakeTime(localHours, localMinutes, 0, 0)); + if (indexPlus != CString::npos) { + localMs = -localMs; + } + } + double day = MakeDay(year, month, date); + double time = MakeTime(hours, minutes, seconds, ms); + double timeValue = TimeClip(MakeDate(day, time)); + if (std::isnan(timeValue)) { + return JSTaggedValue(timeValue); + } + if (isLocal && timeValue < CHINA_1901_MS && (-localMs / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) { + timeValue += static_cast(localMs - CHINA_BEFORE_1901_MS); + } else { + timeValue += localMs; + } + return JSTaggedValue(timeValue); +} + +// static +JSTaggedValue JSDate::UtcParseStringToMs(const CString &str) +{ + int year = 0; + int month = 0; + int date = 1; + int hours = 0; + int minutes = 0; + int seconds = 0; + int ms = 0; + int index = 0; + int len = str.length(); + CString::size_type indexGmt; + CString::size_type indexPlus = CString::npos; + int localTime = 0; + int localHours = 0; + int localMinutes = 0; + int64_t localMs = 0; + bool isLocal = false; + std::array monthName = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + GetNumFromString(str, len, &index, &date); + CString strMonth = str.substr(index + 1, LENGTH_MONTH_NAME); + for (int i = 0; i < MOUTH_PER_YEAR; i++) { + if (strMonth == monthName[i]) { + month = i; + break; + } + } + index += (LENGTH_MONTH_NAME + 1); + GetNumFromString(str, len, &index, &year); + indexGmt = str.find("GMT", index); + if (indexGmt == CString::npos) { + GetNumFromString(str, len, &index, &hours); + GetNumFromString(str, len, &index, &minutes); + GetNumFromString(str, len, &index, &seconds); + isLocal = true; + localMs -= (GetLocalOffsetFromOS(localMs, true) * MS_PER_MINUTE); + } else { + indexPlus = str.find(PLUS, indexGmt); + int indexLocal = static_cast(indexGmt); + GetNumFromString(str, indexGmt, &index, &hours); + GetNumFromString(str, indexGmt, &index, &minutes); + GetNumFromString(str, indexGmt, &index, &seconds); + GetNumFromString(str, len, &indexLocal, &localTime); + localHours = localTime / HUNDRED; + localMinutes = localTime % HUNDRED; + localMs = static_cast(MakeTime(localHours, localMinutes, 0, 0)); + if (indexPlus != CString::npos) { + localMs = -localMs; + } + } + double day = MakeDay(year, month, date); + double time = MakeTime(hours, minutes, seconds, ms); + double timeValue = TimeClip(MakeDate(day, time)); + if (std::isnan(timeValue)) { + return JSTaggedValue(timeValue); + } + if (isLocal && timeValue < CHINA_1901_MS && (-localMs / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) { + timeValue += static_cast(localMs - CHINA_BEFORE_1901_MS); + } else { + timeValue += localMs; + } + return JSTaggedValue(timeValue); +} +// static +JSTaggedValue JSDate::IsoParseStringToMs(const CString &str) +{ + char flag = 0; + int year; + int month = 1; + int date = 1; + int hours = 0; + int minutes = 0; + int seconds = 0; + int ms = 0; + int index = 0; + int len = str.length(); + year = GetSignedNumFromString(str, len, &index); + CString::size_type indexT = str.find(FLAG_TIME, index); + CString::size_type indexZ = str.find(FLAG_UTC, index); + CString::size_type indexEndFlag = 0; + int64_t localMs = 0; + if (indexZ != CString::npos) { + indexEndFlag = indexZ; + } else if (len >= MIN_LENGTH && str.at(len - INDEX_PLUS_NEG) == NEG) { + indexEndFlag = len - INDEX_PLUS_NEG; + flag = NEG; + } else if (len >= MIN_LENGTH && str.at(len - INDEX_PLUS_NEG) == PLUS) { + indexEndFlag = len - INDEX_PLUS_NEG; + flag = PLUS; + } + if (indexT != CString::npos) { + if ((indexT - index) == LENGTH_PER_TIME) { + GetNumFromString(str, len, &index, &month); + } else if ((indexT - index) == (LENGTH_PER_TIME + LENGTH_PER_TIME)) { + GetNumFromString(str, len, &index, &month); + GetNumFromString(str, len, &index, &date); + } + GetNumFromString(str, len, &index, &hours); + GetNumFromString(str, len, &index, &minutes); + if (indexEndFlag > 0) { + if (indexEndFlag - index == LENGTH_PER_TIME) { + GetNumFromString(str, len, &index, &seconds); + } else if (indexEndFlag - index == (LENGTH_PER_TIME + LENGTH_PER_TIME + 1)) { + GetNumFromString(str, len, &index, &seconds); + GetNumFromString(str, len, &index, &ms); + } + } else { + if (len - index == LENGTH_PER_TIME) { + GetNumFromString(str, len, &index, &seconds); + } else if (len - index == (LENGTH_PER_TIME + LENGTH_PER_TIME + 1)) { + GetNumFromString(str, len, &index, &seconds); + GetNumFromString(str, len, &index, &ms); + } + } + } else { + GetNumFromString(str, len, &index, &month); + GetNumFromString(str, len, &index, &date); + } + if (indexEndFlag > 0) { + int localHours = 0; + int localMinutes = 0; + if (indexZ == CString::npos) { + GetNumFromString(str, len, &index, &localHours); + GetNumFromString(str, len, &index, &localMinutes); + if (flag == PLUS) { + localMs = static_cast(-MakeTime(localHours, localMinutes, 0, 0)); + } else { + localMs = static_cast(MakeTime(localHours, localMinutes, 0, 0)); + } + } + } + if (indexEndFlag == 0 && indexT != CString::npos) { + localMs -= (GetLocalOffsetFromOS(localMs, true) * MS_PER_MINUTE); + } + + double day = MakeDay(year, month - 1, date); + double time = MakeTime(hours, minutes, seconds, ms); + double timeValue = TimeClip(MakeDate(day, time)); + if (std::isnan(timeValue)) { + return JSTaggedValue(timeValue); + } + if (flag == 0 && timeValue < CHINA_1901_MS && (-localMs / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) { + timeValue += static_cast(localMs - CHINA_BEFORE_1901_MS); + } else { + timeValue += localMs; + } + return JSTaggedValue(timeValue); +} + +// 20.4.3.2 static +JSTaggedValue JSDate::Parse(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + const CString isoPriStr = "(^|(\\+|-)(\\d{2}))"; + const CString isoDateStr = + "(((\\d{4})-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1]))" + "|((\\d{4})-(0[1-9]|1[0-2]))|(\\d{4}))"; + const CString isoTimeStr = + "((T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])" + "\\.(\\d{3}))|(T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))|" + "(T([01][0-9]|2[0-3]):([0-5][0-9]))|(T([01][0-9]|2[0-3])))" + "($|Z|((\\+|-)(([01][0-9]|2[0-3]):([0-5][0-9]))))"; + const CString isoRegStr = isoPriStr + isoDateStr + "($|Z|(" + isoTimeStr + "))"; + const CString utcDateStr = + "^\\D*(0[1-9]|1[0-9]|2[0-9]|3[0-1]) " + "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\\d{4})"; + const CString timeStr = + "(( ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))|( ([01][0-9]|2[0-3]):([0-5][0-9])))" + "($| *| GMT *| GMT((\\+|-)(\\d{4})) *)"; + const CString utcRegStr = utcDateStr + "($| *| GMT *| GMT((\\+|-)(\\d{4})) *|(" + timeStr + "))"; + const CString localDateStr = + "^[a-zA-Z]* (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) " + "(0[1-9]|1[0-9]|2[0-9]|3[0-1]) (\\d{4})"; + const CString localRegStr = localDateStr + "($| *| GMT *| GMT((\\+|-)(\\d{4})) *|(" + timeStr + "))"; + + std::regex isoReg(isoRegStr); + std::regex utcReg(utcRegStr); + std::regex localReg(localRegStr); + JSHandle msg = base::BuiltinsBase::GetCallArg(argv, 0); + JSHandle str = JSHandle::Cast(JSTaggedValue::ToString(thread, msg)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + CString date = ConvertToString(EcmaString::Cast(str->GetTaggedObject())); + if (std::regex_match(date, isoReg)) { + return IsoParseStringToMs(date); + } + if (std::regex_match(date, utcReg)) { + return UtcParseStringToMs(date); + } + if (std::regex_match(date, localReg)) { + return LocalParseStringToMs(date); + } + return JSTaggedValue(base::NAN_VALUE); +} + +// 20.4.3.1 +JSTaggedValue JSDate::Now() +{ + // time from now is in ms. + int64_t ans; + struct timeval tv { + }; + gettimeofday(&tv, nullptr); + ans = static_cast(tv.tv_sec) * MS_PER_SECOND + (tv.tv_usec / MS_PER_SECOND); + return JSTaggedValue(static_cast(ans)); +} + +// 20.4.4.2 static +JSTaggedValue JSDate::UTC(EcmaRuntimeCallInfo *argv) +{ + double year; + double month = 0; + double date = 1; + double hours = 0; + double minutes = 0; + double seconds = 0; + double ms = 0; + JSThread *thread = argv->GetThread(); + JSHandle yearArg = base::BuiltinsBase::GetCallArg(argv, 0); + JSTaggedNumber yearValue = JSTaggedValue::ToNumber(thread, yearArg); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + if (yearValue.IsNumber()) { + year = yearValue.GetNumber(); + if (std::isfinite(year) && !yearValue.IsInt()) { + year = NumberHelper::TruncateDouble(year); + } + if (year >= 0 && year <= (HUNDRED - 1)) { + year = year + NINETEEN_HUNDRED_YEAR; + } + } else { + year = base::NAN_VALUE; + } + uint32_t index = 1; + uint32_t numArgs = argv->GetArgsNumber(); + JSTaggedValue res; + if (numArgs > index) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, index); + res = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + month = res.GetNumber(); + index++; + } + if (numArgs > index) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, index); + res = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + date = res.GetNumber(); + index++; + } + if (numArgs > index) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, index); + res = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + hours = res.GetNumber(); + index++; + } + if (numArgs > index) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, index); + res = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + minutes = res.GetNumber(); + index++; + } + if (numArgs > index) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, index); + res = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + seconds = res.GetNumber(); + index++; + } + if (numArgs > index) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, index); + res = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + ms = res.GetNumber(); + } + double day = MakeDay(year, month, date); + double time = MakeTime(hours, minutes, seconds, ms); + return JSTaggedValue(TimeClip(MakeDate(day, time))); +} + +int64_t JSDate::GetLocalOffsetFromOS(int64_t timeMs, bool isLocal) +{ + // Preserve the old behavior for non-ICU implementation by ignoring both timeMs and is_utc. + if (!isLocal) { + return 0; + } + timeMs /= JSDate::THOUSAND; + time_t tv = std::time(reinterpret_cast(&timeMs)); + struct tm tm { + }; + // localtime_r is only suitable for linux. + struct tm *t = localtime_r(&tv, &tm); + // tm_gmtoff includes any daylight savings offset. + return t->tm_gmtoff / SEC_PER_MINUTE; +} + +// 20.4.4.10 +JSTaggedValue JSDate::GetTime() const +{ + return GetTimeValue(); +} + +// static +CString JSDate::StrToTargetLength(const CString &str, int length) +{ + int len; + if (str[0] == NEG) { + len = static_cast(str.length() - 1); + } else { + len = static_cast(str.length()); + } + int dif = length - len; + CString sub; + for (int i = 0; i < dif; i++) { + sub += '0'; + } + if (str[0] == NEG) { + sub = NEG + sub + str.substr(1, len); + } else { + sub = sub + str; + } + return sub; +} + +bool JSDate::GetThisDateValues(std::array *date, bool isLocal) const +{ + double timeMs = this->GetTimeValue().GetDouble(); + if (std::isnan(timeMs)) { + return false; + } + GetDateValues(timeMs, date, isLocal); + return true; +} + +// 20.4.4.35 +JSTaggedValue JSDate::ToDateString(JSThread *thread) const +{ + std::array monthName = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + std::array weekdayName = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + std::array fields = {0}; + if (!GetThisDateValues(&fields, true)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString year = StrToTargetLength(ToCString(fields[YEAR]), STR_LENGTH_YEAR); + CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS); + CString str = weekdayName[fields[WEEKDAY]] + SPACE + monthName[fields[MONTH]] + SPACE + day + SPACE + year; + JSHandle result = thread->GetEcmaVM()->GetFactory()->NewFromString(str); + return result.GetTaggedValue(); +} + +// static +CString JSDate::ToDateString(double timeMs) +{ + if (std::isnan(timeMs)) { + return "Invalid Date"; + } + std::array monthName = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + std::array weekdayName = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + std::array fields = {0}; + GetDateValues(timeMs, &fields, true); + CString localTime; + int localMin = 0; + localMin = GetLocalOffsetFromOS(localMin, true); + if (timeMs < CHINA_BEFORE_1900_MS && localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else if (localMin < 0) { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + localTime = localTime + StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + CString year = ToCString(fields[YEAR]); + year = StrToTargetLength(year, STR_LENGTH_YEAR); + CString weekday = weekdayName[fields[WEEKDAY]]; + CString month = monthName[fields[MONTH]]; + CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS); + CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS); + CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS); + CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS); + CString str = weekday + SPACE + month + SPACE + day + SPACE + year + SPACE + hour + COLON + minute + COLON + + second + SPACE + "GMT" + localTime; + return str; +} +// 20.4.4.36 +JSTaggedValue JSDate::ToISOString(JSThread *thread) const +{ + std::array fields = {0}; + if (!GetThisDateValues(&fields, false)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString year = ToCString(fields[YEAR]); + if (year[0] == NEG) { + year = StrToTargetLength(year, STR_LENGTH_YEAR + STR_LENGTH_OTHERS); + } else if (year.length() > STR_LENGTH_YEAR) { + year = PLUS + StrToTargetLength(year, STR_LENGTH_YEAR + STR_LENGTH_OTHERS); + } else { + year = StrToTargetLength(year, STR_LENGTH_YEAR); + } + CString month = StrToTargetLength(ToCString(fields[MONTH] + 1), STR_LENGTH_OTHERS); + CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS); + CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS); + CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS); + CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS); + CString ms = StrToTargetLength(ToCString(fields[MS]), STR_LENGTH_OTHERS + 1); + CString str = + year + NEG + month + NEG + day + FLAG_TIME + hour + COLON + minute + COLON + second + POINT + ms + FLAG_UTC; + return thread->GetEcmaVM()->GetFactory()->NewFromString(str).GetTaggedValue(); +} + +CString JSDate::GetLocaleTimeStr(const std::array &fields) const +{ + CString hour; + if (fields[HOUR] > MOUTH_PER_YEAR) { + hour = ToCString(fields[HOUR] - MOUTH_PER_YEAR); + } else { + hour = ToCString(fields[HOUR]); + } + CString minute = ToCString(fields[MIN]); + CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS); + CString str = hour + COLON + minute + COLON + second; + if (fields[HOUR] >= MOUTH_PER_YEAR) { + str = "下午" + str; + } else { + str = "上午" + str; + } + return str; +} + +CString JSDate::GetLocaleDateStr(const std::array &fields) const +{ + CString year; + if (fields[YEAR] < 0) { + year = ToCString(-fields[YEAR] + 1); + } else { + year = ToCString(fields[YEAR]); + } + CString month = ToCString(fields[MONTH] + 1); + CString day = ToCString(fields[DAYS]); + CString str = year + VIRGULE + month + VIRGULE + day; + return str; +} + +// 20.4.4.38 +JSTaggedValue JSDate::ToLocaleDateString(JSThread *thread) const +{ + std::array fields = {0}; + if (!GetThisDateValues(&fields, true)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString str = GetLocaleDateStr(fields); + return thread->GetEcmaVM()->GetFactory()->NewFromString(str).GetTaggedValue(); +} + +// 20.4.4.39 +JSTaggedValue JSDate::ToLocaleString(JSThread *thread) const +{ + std::array fields = {0}; + if (!GetThisDateValues(&fields, true)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString strDate = GetLocaleDateStr(fields); + CString strTime = GetLocaleTimeStr(fields); + return thread->GetEcmaVM()->GetFactory()->NewFromString(strDate + SPACE + strTime).GetTaggedValue(); +} + +// 20.4.4.40 +JSTaggedValue JSDate::ToLocaleTimeString(JSThread *thread) const +{ + std::array fields = {0}; + if (!GetThisDateValues(&fields, true)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString str = GetLocaleTimeStr(fields); + return thread->GetEcmaVM()->GetFactory()->NewFromString(str).GetTaggedValue(); +} + +// 20.4.4.41 +JSTaggedValue JSDate::ToString(JSThread *thread) const +{ + std::array weekdayName = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + std::array monthName = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + int localMin = 0; + std::array fields = {0}; + if (!GetThisDateValues(&fields, true)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString localTime; + localMin = GetLocalOffsetFromOS(localMin, true); + if (static_cast(this->GetTimeValue().GetDouble()) < CHINA_BEFORE_1900_MS && + localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else if (localMin < 0) { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + localTime = localTime + StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + CString year = ToCString(fields[YEAR]); + year = StrToTargetLength(year, STR_LENGTH_YEAR); + CString weekday = weekdayName[fields[WEEKDAY]]; + CString month = monthName[fields[MONTH]]; + CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS); + CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS); + CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS); + CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS); + CString str = weekday + SPACE + month + SPACE + day + SPACE + year + SPACE + hour + COLON + minute + COLON + + second + SPACE + "GMT" + localTime; + return thread->GetEcmaVM()->GetFactory()->NewFromString(str).GetTaggedValue(); +} + +// 20.4.4.42 +JSTaggedValue JSDate::ToTimeString(JSThread *thread) const +{ + int localMin = 0; + std::array fields = {0}; + if (!GetThisDateValues(&fields, true)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString localTime; + localMin = GetLocalOffsetFromOS(localMin, true); + if (static_cast(this->GetTimeValue().GetDouble()) < CHINA_BEFORE_1900_MS && + localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + localTime = localTime + StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS); + CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS); + CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS); + CString str = hour + COLON + minute + COLON + second + SPACE + "GMT" + localTime; + return thread->GetEcmaVM()->GetFactory()->NewFromString(str).GetTaggedValue(); +} + +// 20.4.4.43 +JSTaggedValue JSDate::ToUTCString(JSThread *thread) const +{ + std::array weekdayName = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + std::array monthName = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + std::array fields = {0}; + if (!GetThisDateValues(&fields, false)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString year = ToCString(fields[YEAR]); + year = StrToTargetLength(year, STR_LENGTH_YEAR); + CString weekday = weekdayName[fields[WEEKDAY]]; + CString month = monthName[fields[MONTH]]; + CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS); + CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS); + CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS); + CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS); + CString ms = StrToTargetLength(ToCString(fields[MS]), STR_LENGTH_OTHERS); + CString str = weekday + COMMA + SPACE + day + SPACE + month + SPACE + year + SPACE + hour + COLON + minute + COLON + + second + SPACE + "GMT"; + return thread->GetEcmaVM()->GetFactory()->NewFromString(str).GetTaggedValue(); +} + +// 20.4.4.44 +JSTaggedValue JSDate::ValueOf() const +{ + return this->GetTimeValue(); +} + +// static +void JSDate::GetDateValues(double timeMs, std::array *date, bool isLocal) +{ + int64_t tz = 0; + int64_t timeMsInt; + int month = 0; + timeMsInt = static_cast(timeMs); + if (isLocal) { // timezone offset + tz = GetLocalOffsetFromOS(timeMsInt, isLocal); + if (timeMsInt < CHINA_BEFORE_1900_MS && tz == CHINA_AFTER_1901_MIN) { + timeMsInt += CHINA_BEFORE_1901_ADDMS; + timeMsInt += tz * MS_PER_SECOND * SEC_PER_MINUTE; + tz = CHINA_BEFORE_1901_MIN; + } else { + timeMsInt += tz * MS_PER_SECOND * SEC_PER_MINUTE; + } + } + + DateUtils::TransferTimeToDate(timeMsInt, date); + + int index = DateUtils::IsLeap((*date)[YEAR]) ? 1 : 0; + int left = 0; + int right = MONTH_PER_YEAR; + while (left <= right) { + int middle = (left + right) / 2; // 2 : half + if (DAYS_FROM_MONTH[index][middle] <= (*date)[DAYS] && DAYS_FROM_MONTH[index][middle + 1] > (*date)[DAYS]) { + month = middle; + (*date)[DAYS] -= DAYS_FROM_MONTH[index][month]; + break; + } else if ((*date)[DAYS] > DAYS_FROM_MONTH[index][middle]) { // NOLINT(readability-else-after-return) + left = middle + 1; + } else if ((*date)[DAYS] < DAYS_FROM_MONTH[index][middle]) { + right = middle - 1; + } + } + + (*date)[MONTH] = month; + (*date)[DAYS] = (*date)[DAYS] + 1; + (*date)[TIMEZONE] = -tz; +} + +double JSDate::GetDateValue(double timeMs, uint8_t code, bool isLocal) const +{ + if (std::isnan(timeMs)) { + return base::NAN_VALUE; + } + std::array date = {0}; + GetDateValues(timeMs, &date, isLocal); + return static_cast(date[code]); +} + +JSTaggedValue JSDate::SetDateValue(EcmaRuntimeCallInfo *argv, uint32_t code, bool isLocal) const +{ + // get date values. + std::array date = {0}; + double timeMs = this->GetTimeValue().GetDouble(); + + // get values from argv. + uint32_t argc = argv->GetArgsNumber(); + if (argc == 0) { + return JSTaggedValue(base::NAN_VALUE); + } + + uint32_t firstValue = code & CODE_FLAG; + uint32_t endValue = (code >> CODE_4_BIT) & CODE_FLAG; + uint32_t count = endValue - firstValue; + + if (argc < count) { + count = argc; + } + + if (std::isnan(timeMs) && firstValue == 0) { + timeMs = 0.0; + GetDateValues(timeMs, &date, false); + } else { + GetDateValues(timeMs, &date, isLocal); + } + + for (uint32_t i = 0; i < count; i++) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, i); + JSThread *thread = argv->GetThread(); + JSTaggedNumber res = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double temp = res.GetNumber(); + if (std::isnan(temp)) { + return JSTaggedValue(base::NAN_VALUE); + } + date[firstValue + i] = NumberHelper::TruncateDouble(temp); + } + // set date values. + return JSTaggedValue(SetDateValues(&date, isLocal)); +} + +// static +double JSDate::SetDateValues(const std::array *date, bool isLocal) +{ + int64_t month = DateUtils::Mod((*date)[MONTH], MONTH_PER_YEAR); + int64_t year = (*date)[YEAR] + ((*date)[MONTH] - month) / MONTH_PER_YEAR; + int64_t days = DateUtils::GetDaysFromYear(year); + int index = DateUtils::IsLeap(year) ? 1 : 0; + days += DAYS_FROM_MONTH[index][month]; + + days += (*date)[DAYS] - 1; + int64_t millisecond = + (((*date)[HOUR] * MIN_PER_HOUR + (*date)[MIN]) * SEC_PER_MINUTE + (*date)[SEC]) * MS_PER_SECOND + (*date)[MS]; + int64_t result = days * MS_PER_DAY + millisecond; + if (isLocal) { + int64_t offset = GetLocalOffsetFromOS(result, isLocal) * SEC_PER_MINUTE * MS_PER_SECOND; + if (result < CHINA_1901_MS && (offset / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) { + offset += CHINA_BEFORE_1901_ADDMS; + } + result -= offset; + } + return TimeClip(result); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_date.h b/ecmascript/js_date.h new file mode 100644 index 0000000000000000000000000000000000000000..ccaf4a8127db0220dfdaf057df540e0fc21d95f4 --- /dev/null +++ b/ecmascript/js_date.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSDATE_H +#define PANDA_RUNTIME_ECMASCRIPT_JSDATE_H + +#include + +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/js_tagged_value-inl.h" + +namespace panda::ecmascript { +static constexpr int64_t DAYS_IN_YEAR = 365; +static constexpr std::array APPROXIMATION_NUMBER = {100000, 3652425}; +static constexpr int64_t CHINA_BEFORE_1900_MS = -2177481943000; +static constexpr int64_t CHINA_1901_MS = -2177452800000; +static constexpr int CHINA_BEFORE_1901_ADDMS = 343000; +static constexpr int MS_PER_SECOND = 1000; +static constexpr int SEC_PER_MINUTE = 60; +static constexpr int MIN_PER_HOUR = 60; +static constexpr int MS_PER_HOUR = 3600 * 1000; +static constexpr int MS_PER_DAY = 86400000; +static constexpr int DAY_PER_WEEK = 7; +static constexpr int DATE_LENGTH = 9; +// the index in the Date Fields +static constexpr uint8_t YEAR = 0; +static constexpr uint8_t MONTH = 1; +static constexpr uint8_t DAYS = 2; +static constexpr uint8_t HOUR = 3; +static constexpr uint8_t MIN = 4; +static constexpr uint8_t SEC = 5; +static constexpr uint8_t MS = 6; +static constexpr uint8_t WEEKDAY = 7; +static constexpr uint8_t TIMEZONE = 8; +static constexpr int CHINA_BEFORE_1901_MIN = 485; +static constexpr int CHINA_AFTER_1901_MIN = 480; +static constexpr int CHINA_BEFORE_1901_MS = 343000; +static constexpr std::array LEAP_NUMBER = {4, 100, 400}; +static constexpr std::array YEAR_NUMBER = {1970, 1969, 1901, 1601}; + +class DateUtils { +public: + static void TransferTimeToDate(int64_t timeMs, std::array *date); + static int64_t Mod(int64_t a, int b); + static bool IsLeap(int64_t year); + static int64_t GetDaysInYear(int64_t year); + static int64_t GetDaysFromYear(int64_t year); + // return the year, update days. + static int64_t GetYearFromDays(int64_t *days); + static int64_t FloorDiv(int64_t a, int64_t b); +}; +class JSDate : public JSObject { +public: + static JSDate *Cast(ObjectHeader *object) + { + return static_cast(object); + } + static constexpr size_t TIME_VALUE_OFFSET = JSObject::SIZE; + ACCESSORS(TimeValue, TIME_VALUE_OFFSET, LOCAL_TIME_OFFSET) + ACCESSORS(LocalOffset, LOCAL_TIME_OFFSET, SIZE) // localoffset in min + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, TIME_VALUE_OFFSET, SIZE) + + static double MakeDay(double year, double month, double date); + static double MakeTime(double hour, double min, double sec, double ms); + static double MakeDate(double day, double time); + static double TimeClip(double time); + static JSTaggedValue LocalParseStringToMs(const CString &str); + static JSTaggedValue UtcParseStringToMs(const CString &str); + static JSTaggedValue IsoParseStringToMs(const CString &str); + static int GetSignedNumFromString(const CString &str, int len, int *index); + static bool GetNumFromString(const CString &str, int len, int *index, int *num); + + // 20.4.1.7 + int64_t GetLocalOffsetInMin(const JSThread *thread, int64_t timeMs, bool isLocal); + + // 20.4.1.8 + double LocalTime(double timeMs) const; + + // 20.4.1.9 + double UTCTime(double timeMs) const; + + // 20.4.3.1 + static JSTaggedValue Now(); + + // 20.4.3.2 + static JSTaggedValue Parse(EcmaRuntimeCallInfo *argv); + + // 20.4.3.4 + static JSTaggedValue UTC(EcmaRuntimeCallInfo *argv); + + // 20.4.4.10 + JSTaggedValue GetTime() const; + + // 20.4.4.19 + JSTaggedValue GetUTCSeconds(); + + // 20.4.4.35 + JSTaggedValue ToDateString(JSThread *thread) const; + static CString ToDateString(double timeMs); + + // 20.4.4.36 + JSTaggedValue ToISOString(JSThread *thread) const; + + // 20.4.4.38 + JSTaggedValue ToLocaleDateString(JSThread *thread) const; + + // 20.4.4.39 + JSTaggedValue ToLocaleString(JSThread *thread) const; + + // 20.4.4.40 + JSTaggedValue ToLocaleTimeString(JSThread *thread) const; + + // 20.4.4.41 + JSTaggedValue ToString(JSThread *thread) const; + + // 20.4.4.42 + JSTaggedValue ToTimeString(JSThread *thread) const; + + // 20.4.4.43 + JSTaggedValue ToUTCString(JSThread *thread) const; + + // 20.4.4.44 + JSTaggedValue ValueOf() const; + + JSTaggedValue SetDateValue(EcmaRuntimeCallInfo *argv, uint32_t code, bool isLocal) const; + double GetDateValue(double timeMs, uint8_t code, bool isLocal) const; + + static constexpr double MAX_DOUBLE = std::numeric_limits::max(); + static constexpr double MAX_INT = std::numeric_limits::max(); + static constexpr uint16_t NINETEEN_HUNDRED_YEAR = 1900; + static constexpr uint16_t HUNDRED = 100; + static constexpr uint16_t THOUSAND = 1000; + static double SetDateValues(const std::array *date, bool isLocal); + static void GetDateValues(double timeMs, std::array *date, bool isLocal); + static int64_t GetLocalOffsetFromOS(int64_t timeMs, bool isLocal); + static CString StrToTargetLength(const CString &str, int length); + DECL_DUMP() + +private: + bool GetThisDateValues(std::array *date, bool isLocal) const; + CString GetLocaleTimeStr(const std::array &fields) const; + CString GetLocaleDateStr(const std::array &fields) const; + static int64_t MathMod(int64_t a, int b); + + static constexpr int MINUTE_PER_HOUR = 60; + static constexpr int MOUTH_PER_YEAR = 12; + static constexpr std::array MONTH_DAYS = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + static constexpr int DAYS_FROM_MONTH [2][13] = { + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} + }; + static constexpr int STR_LENGTH_YEAR = 4; + static constexpr int STR_LENGTH_OTHERS = 2; + static constexpr int MONTH_PER_YEAR = 12; + static constexpr int YEAR_DELTA = 399999; + static constexpr int LINE_YEAR = 1970; + static constexpr int CENTURY = 100; + static constexpr char NEG = '-'; + static constexpr char PLUS = '+'; + static constexpr char SPACE = ' '; + static constexpr char COLON = ':'; + static constexpr char POINT = '.'; + static constexpr int LENGTH_MONTH_NAME = 3; + static constexpr int MS_PER_MINUTE = 60000; + static constexpr int64_t MAX_TIME_IN_MS = static_cast(864000000) * 10000000; + static constexpr int TEN = 10; + static constexpr char FLAG_TIME = 'T'; + static constexpr char FLAG_UTC = 'Z'; + static constexpr char VIRGULE = '/'; + static constexpr char COMMA = ','; + static constexpr int LENGTH_PER_TIME = 3; + static constexpr int MIN_LENGTH = 10; + static constexpr int INDEX_PLUS_NEG = 6; + static constexpr int NUM_NINE = 9; + static constexpr int ORIGIN_YEAR = 1901; + + static constexpr uint32_t CODE_FLAG = 0x0FULL; + static constexpr size_t CODE_4_BIT = 4; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSDATE_H diff --git a/ecmascript/js_float32_array.h b/ecmascript/js_float32_array.h new file mode 100644 index 0000000000000000000000000000000000000000..ff44b0b785ec554f8da6c498201ab46ea1cf8da2 --- /dev/null +++ b/ecmascript/js_float32_array.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_FLOAT32_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_FLOAT32_ARRAY_H + +#include +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class JSFloat32Array : public JSObject { +public: + static JSFloat32Array *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSFloat32Array()); + return static_cast(object); + } + + static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; + ACCESSORS(ViewedArrayBuffer, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) + ACCESSORS(TypedArrayName, TYPED_ARRAY_NAME_OFFSET, BYTE_LENGTH_OFFSET) + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET) + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, ARRAY_LENGTH_OFFSET) + ACCESSORS(ArrayLength, ARRAY_LENGTH_OFFSET, SIZE) + + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VIEWED_ARRAY_BUFFER_OFFSET, SIZE) +}; // namespace panda::ecmascript +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_FLOAT32_ARRAY_H diff --git a/ecmascript/js_float64_array.h b/ecmascript/js_float64_array.h new file mode 100644 index 0000000000000000000000000000000000000000..1e248e6bf0f68d8df02eda0cc79daa5ed0e4fa5f --- /dev/null +++ b/ecmascript/js_float64_array.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_FLOAT64_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_FLOAT64_ARRAY_H + +#include +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class JSFloat64Array : public JSObject { +public: + static JSFloat64Array *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSFloat64Array()); + return static_cast(object); + } + + static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; + ACCESSORS(ViewedArrayBuffer, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) + ACCESSORS(TypedArrayName, TYPED_ARRAY_NAME_OFFSET, BYTE_LENGTH_OFFSET) + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET) + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, ARRAY_LENGTH_OFFSET) + ACCESSORS(ArrayLength, ARRAY_LENGTH_OFFSET, SIZE) + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VIEWED_ARRAY_BUFFER_OFFSET, SIZE) +}; // namespace panda::ecmascript +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_FLOAT64_ARRAY_H diff --git a/ecmascript/js_for_in_iterator.cpp b/ecmascript/js_for_in_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1876c2e6beae8c6fefcb0a239de41b833ceb8ce3 --- /dev/null +++ b/ecmascript/js_for_in_iterator.cpp @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_for_in_iterator.h" + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/global_dictionary-inl.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tagged_dictionary.h" +#include "ecmascript/tagged_queue-inl.h" +#include "ecmascript/tagged_queue.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; + +bool JSForInIterator::CheckObjProto(const JSThread *thread, const JSHandle &it) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle object(thread, it->GetObject()); + if (!object->IsJSObject()) { + return false; + } + auto *hclass = object->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (jsType != JSType::JS_OBJECT) { + return false; + } + JSTaggedValue proto = hclass->GetPrototype(); + if (!proto.IsJSObject()) { + return false; + } + return hclass->GetPrototype().GetTaggedObject()->GetClass() == + env->GetObjectFunctionPrototypeClass().GetTaggedValue().GetTaggedObject()->GetClass(); +} + +void JSForInIterator::FastGetAllEnumKeys(const JSThread *thread, const JSHandle &it, + const JSHandle &object) +{ + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj(object); + uint32_t numOfElements = obj->GetNumberOfElements(); + uint32_t numOfKeys = obj->GetNumberOfKeys(); + JSHandle remaining = factory->NewTaggedQueue(numOfElements + numOfKeys + 1); + if (numOfElements > 0) { + uint32_t elementIndex = 0; + if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { + elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength(); + for (uint32_t i = 0; i < elementIndex; i++) { + value.Update(factory->NewFromString(ToCString(i)).GetTaggedValue()); + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } else { + JSHandle elements(thread, obj->GetElements()); + if (!elements->IsDictionaryMode()) { + uint32_t elementsLen = elements->GetLength(); + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!elements->Get(i).IsHole()) { + value.Update(factory->NewFromString(ToCString(i)).GetTaggedValue()); + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } else { + JSHandle numberDic(elements); + int size = numberDic->Size(); + CVector sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = numberDic->GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyAttributes attr = numberDic->GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + sortArr.push_back(JSTaggedValue(static_cast(key.GetInt()))); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), NumberDictionary::CompKey); + for (const auto &entry : sortArr) { + value.Update(factory->NewFromString(ToCString(entry.GetInt())).GetTaggedValue()); + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } + } + if (numOfKeys > 0) { + if (obj->IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject()); + int size = dict->Size(); + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = dict->GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyAttributes attr = dict->GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + std::pair pair(key, attr.GetOffset()); + sortArr.emplace_back(pair); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), GlobalDictionary::CompKey); + for (const auto &entry : sortArr) { + JSTaggedValue nameKey = entry.first; + if (nameKey.IsString()) { + value.Update(nameKey); + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } else { + JSHandle propertiesArr(thread, obj->GetProperties()); + if (!propertiesArr->IsDictionaryMode()) { + JSHClass *jsHclass = obj->GetJSHClass(); + JSTaggedValue enumCache = jsHclass->GetEnumCache(); + if (!enumCache.IsNull()) { + JSHandle cache(thread, enumCache); + uint32_t length = cache->GetLength(); + if (length != numOfKeys) { + JSHandle layoutInfoHandle(thread, jsHclass->GetAttributes()); + for (uint32_t i = 0; i < numOfKeys; i++) { + JSTaggedValue key = layoutInfoHandle->GetKey(i); + if (key.IsString()) { + value.Update(key); + if (layoutInfoHandle->GetAttr(i).IsEnumerable()) { + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } + } else { + for (uint32_t i = 0; i < length; i++) { + JSTaggedValue key = cache->Get(i); + if (key.IsString()) { + value.Update(key); + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } + } else { + JSHandle layoutInfoHandle(thread, jsHclass->GetAttributes()); + for (uint32_t i = 0; i < numOfKeys; i++) { + JSTaggedValue key = layoutInfoHandle->GetKey(i); + if (key.IsString()) { + value.Update(key); + if (layoutInfoHandle->GetAttr(i).IsEnumerable()) { + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } + } + } else { + JSHandle nameDic(propertiesArr); + int size = nameDic->Size(); + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = nameDic->GetKey(hashIndex); + if (key.IsString()) { + PropertyAttributes attr = nameDic->GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + std::pair pair(key, attr); + sortArr.emplace_back(pair); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), NameDictionary::CompKey); + for (const auto &entry : sortArr) { + value.Update(entry.first); + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } + } + it->SetRemainingKeys(thread, remaining); + it->SetWasVisited(thread, JSTaggedValue::True()); +} + +void JSForInIterator::SlowGetAllEnumKeys(JSThread *thread, const JSHandle &it, + const JSHandle &object) +{ + JSMutableHandle visited(thread, it->GetVisitedKeys()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + JSMutableHandle remaining(thread, it->GetRemainingKeys()); + JSHandle arr = JSTaggedValue::GetOwnPropertyKeys(thread, object); + array_size_t len = arr->GetLength(); + for (array_size_t i = 0; i < len; i++) { + value.Update(arr->Get(i)); + if (value->IsString()) { + remaining.Update(JSTaggedValue(TaggedQueue::Push(thread, remaining, value))); + } + } + it->SetRemainingKeys(thread, remaining); + it->SetVisitedKeys(thread, visited); + it->SetWasVisited(thread, JSTaggedValue::True()); +} + +std::pair JSForInIterator::NextInternal(JSThread *thread, const JSHandle &it) +{ + bool notModiObjProto = true; + notModiObjProto = CheckObjProto(thread, it); + while (true) { + JSHandle object(thread, it->GetObject()); + if (object->IsNull() || object->IsUndefined()) { + return std::make_pair(JSTaggedValue::Undefined(), true); + } + if (it->GetWasVisited().IsFalse()) { + if (object->IsJSObject() && notModiObjProto) { + FastGetAllEnumKeys(thread, it, object); + } else { + SlowGetAllEnumKeys(thread, it, object); + } + } + + JSHandle remaining(thread, it->GetRemainingKeys()); + JSMutableHandle visited(thread, it->GetVisitedKeys()); + while (!remaining->Empty()) { + JSTaggedValue r = remaining->Pop(thread); + if (object->IsJSObject() && notModiObjProto) { + return std::make_pair(r, false); + } + JSHandle key(thread, r); + bool has_same = false; + array_size_t len = visited->Size(); + for (array_size_t i = 0; i < len; i++) { + if (JSTaggedValue::SameValue(r, visited->Get(i))) { + has_same = true; + break; + } + } + if (has_same) { + continue; + } + PropertyDescriptor desc(thread); + bool has = JSTaggedValue::GetOwnProperty(thread, object, key, desc); + if (has) { + auto queue = JSTaggedValue(TaggedQueue::Push(thread, visited, key)); + visited.Update(queue); + it->SetVisitedKeys(thread, queue); + if (desc.IsEnumerable()) { + return std::make_pair(key.GetTaggedValue(), false); + } + } + } + if (notModiObjProto) { + return std::make_pair(JSTaggedValue::Undefined(), true); + } + JSTaggedValue proto = JSHandle::Cast(object)->GetPrototype(thread); + it->SetObject(thread, proto); + it->SetWasVisited(thread, JSTaggedValue::False()); + } +} + +// 13.7.5.16.2.1 %ForInIteratorPrototype%.next ( ) +JSTaggedValue JSForInIterator::Next(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle it(BuiltinsBase::GetThis(msg)); + ASSERT(it->IsForinIterator()); + std::pair res = NextInternal(thread, it); + return res.first; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_for_in_iterator.h b/ecmascript/js_for_in_iterator.h new file mode 100644 index 0000000000000000000000000000000000000000..d8b45fc67727ee2f32df4083e7819493dcc7d188 --- /dev/null +++ b/ecmascript/js_for_in_iterator.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_FORIN_ITERATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_FORIN_ITERATOR_H + +#include +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/tagged_array-inl.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSForInIterator : public JSObject { +public: + static JSForInIterator *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static std::pair NextInternal(JSThread *thread, const JSHandle &it); + + static JSTaggedValue Next(EcmaRuntimeCallInfo *msg); + + static bool CheckObjProto(const JSThread *thread, const JSHandle &it); + + static void FastGetAllEnumKeys(const JSThread *thread, const JSHandle &it, + const JSHandle &object); + + static void SlowGetAllEnumKeys(JSThread *thread, const JSHandle &it, + const JSHandle &object); + + static constexpr size_t OBJECT_OFFSET = JSObject::SIZE; + + ACCESSORS(Object, OBJECT_OFFSET, WAS_VISITED_OFFSET) + + ACCESSORS(WasVisited, WAS_VISITED_OFFSET, VISITED_KEYS_OFFSET) + + ACCESSORS(VisitedKeys, VISITED_KEYS_OFFSET, REMAINING_KEYS_OFFSET) + + ACCESSORS(RemainingKeys, REMAINING_KEYS_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, OBJECT_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_FORIN_ITERATOR_H diff --git a/ecmascript/js_function.cpp b/ecmascript/js_function.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0e650c46aa4c7fadbb7082bc18b4ec9596b5eca8 --- /dev/null +++ b/ecmascript/js_function.cpp @@ -0,0 +1,681 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_function.h" + +#include "ecmascript/base/error_type.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/global_env.h" +#include "ecmascript/interpreter/interpreter-inl.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_proxy.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array.h" + +namespace panda::ecmascript { +void JSFunction::InitializeJSFunction(JSThread *thread, const JSHandle &func, FunctionKind kind, + bool strict) +{ + FunctionMode thisMode; + if (IsArrowFunction(kind)) { + thisMode = FunctionMode::LEXICAL; + } else if (strict) { + thisMode = FunctionMode::STRICT; + } else { + thisMode = FunctionMode::GLOBAL; + } + + int32_t flag = FunctionKindBit::Encode(kind) | StrictBit::Encode(strict) | ThisModeBit::Encode(thisMode); + func->SetProtoOrDynClass(thread, JSTaggedValue::Hole(), SKIP_BARRIER); + func->SetHomeObject(thread, JSTaggedValue::Undefined(), SKIP_BARRIER); + func->SetLexicalEnv(thread, JSTaggedValue::Undefined(), SKIP_BARRIER); + func->SetConstantPool(thread, JSTaggedValue::Undefined(), SKIP_BARRIER); + func->SetProfileTypeInfo(thread, JSTaggedValue::Undefined(), SKIP_BARRIER); + func->SetFunctionExtraInfo(thread, JSTaggedValue::Undefined()); + func->SetFunctionInfoFlag(thread, JSTaggedValue(flag)); + + ASSERT(!func->IsPropertiesDict()); + auto globalConst = thread->GlobalConstants(); + if (HasPrototype(kind)) { + JSHandle accessor = globalConst->GetHandledFunctionPrototypeAccessor(); + if (kind == FunctionKind::BASE_CONSTRUCTOR || kind == FunctionKind::GENERATOR_FUNCTION) { + func->SetPropertyInlinedProps(thread, PROTOTYPE_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + accessor = globalConst->GetHandledFunctionNameAccessor(); + func->SetPropertyInlinedProps(thread, NAME_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + accessor = globalConst->GetHandledFunctionLengthAccessor(); + func->SetPropertyInlinedProps(thread, LENGTH_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + } else if (JSFunction::IsClassConstructor(kind)) { + func->SetPropertyInlinedProps(thread, CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + accessor = globalConst->GetHandledFunctionLengthAccessor(); + func->SetPropertyInlinedProps(thread, LENGTH_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + } else { + PropertyDescriptor desc(thread, accessor, kind != FunctionKind::BUILTIN_CONSTRUCTOR, false, false); + [[maybe_unused]] bool success = JSObject::DefineOwnProperty(thread, JSHandle(func), + globalConst->GetHandledPrototypeString(), desc); + ASSERT(success); + } + } else if (kind == FunctionKind::NORMAL_FUNCTION) { + JSHandle accessor = globalConst->GetHandledFunctionNameAccessor(); + func->SetPropertyInlinedProps(thread, NAME_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + accessor = globalConst->GetHandledFunctionLengthAccessor(); + func->SetPropertyInlinedProps(thread, LENGTH_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + } +} + +JSHandle JSFunction::NewJSFunctionPrototype(JSThread *thread, ObjectFactory *factory, + const JSHandle &func) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle objFun = env->GetObjectFunction(); + JSHandle funPro = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); + func->SetFunctionPrototype(thread, funPro.GetTaggedValue()); + + // set "constructor" in prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor descriptor(thread, JSHandle::Cast(func), true, false, true); + JSObject::DefineOwnProperty(thread, funPro, constructorKey, descriptor); + + return funPro; +} + +JSHClass *JSFunction::GetOrCreateInitialJSHClass(JSThread *thread, const JSHandle &fun) +{ + JSTaggedValue protoOrDyn(fun->GetProtoOrDynClass()); + if (protoOrDyn.IsJSHClass()) { + return reinterpret_cast(protoOrDyn.GetTaggedObject()); + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle proto; + if (!fun->HasFunctionPrototype()) { + proto = JSHandle::Cast(NewJSFunctionPrototype(thread, factory, fun)); + } else { + proto = JSHandle(thread, fun->GetProtoOrDynClass()); + } + + JSHandle dynclass = factory->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, proto); + fun->SetProtoOrDynClass(thread, dynclass); + return *dynclass; +} + +JSTaggedValue JSFunction::PrototypeGetter(JSThread *thread, const JSHandle &self) +{ + JSHandle func = JSHandle::Cast(self); + if (!func->HasFunctionPrototype()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + NewJSFunctionPrototype(thread, factory, func); + } + return JSFunction::Cast(*self)->GetFunctionPrototype(); +} + +bool JSFunction::PrototypeSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + [[maybe_unused]] bool mayThrow) +{ + JSHandle func(self); + JSTaggedValue protoOrDyn = func->GetProtoOrDynClass(); + if (protoOrDyn.IsJSHClass()) { + // need transtion + JSHandle dynclass(thread, protoOrDyn); + JSHandle newDynclass = JSHClass::TransitionProto(thread, dynclass, value); + if (value->IsECMAObject()) { + JSObject::Cast(value->GetTaggedObject())->GetJSHClass()->SetIsPrototype(true); + } + func->SetProtoOrDynClass(thread, newDynclass); + } else { + func->SetFunctionPrototype(thread, value.GetTaggedValue()); + } + return true; +} + +JSTaggedValue JSFunction::NameGetter(JSThread *thread, const JSHandle &self) +{ + JSMethod *target = JSHandle::Cast(self)->GetCallTarget(); + if (target->GetPandaFile() == nullptr) { + return JSTaggedValue::Undefined(); + } + CString funcName( + utf::Mutf8AsCString(target->GetStringDataAnnotation(JSMethod::AnnotationField::FUNCTION_NAME).data)); + if (funcName.empty()) { + return thread->GlobalConstants()->GetEmptyString(); + } + if (JSHandle::Cast(self)->GetFunctionKind() == FunctionKind::GETTER_FUNCTION) { + funcName.insert(0, "get "); + } + if (JSHandle::Cast(self)->GetFunctionKind() == FunctionKind::SETTER_FUNCTION) { + funcName.insert(0, "set "); + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + return factory->NewFromString(funcName).GetTaggedValue(); +} + +JSTaggedValue JSFunction::LengthGetter([[maybe_unused]] JSThread *thread, const JSHandle &self) +{ + JSMethod *target = JSHandle::Cast(self)->GetCallTarget(); + if (target->GetPandaFile() == nullptr) { + return JSTaggedValue::Undefined(); + } + return JSTaggedValue(target->GetNumericalAnnotation(Method::AnnotationField::FUNCTION_LENGTH)); +} + +bool JSFunction::OrdinaryHasInstance(JSThread *thread, const JSHandle &constructor, + const JSHandle &obj) +{ + // 1. If IsCallable(C) is false, return false. + if (!constructor->IsCallable()) { + return false; + } + + // 2. If C has a [[BoundTargetFunction]] internal slot, then + // a. Let BC be the value of C's [[BoundTargetFunction]] internal slot. + // b. Return InstanceofOperator(O,BC) (see 12.9.4). + if (constructor->IsBoundFunction()) { + JSHandle boundFunction(thread, JSBoundFunction::Cast(constructor->GetTaggedObject())); + JSTaggedValue boundTarget = boundFunction->GetBoundTarget(); + return JSObject::InstanceOf(thread, obj, JSHandle(thread, boundTarget)); + } + // 3. If Type(O) is not Object, return false + if (!obj->IsECMAObject()) { + return false; + } + + // 4. Let P be Get(C, "prototype"). + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle constructorPrototype = + JSTaggedValue::GetProperty(thread, constructor, globalConst->GetHandledPrototypeString()).GetValue(); + + // 5. ReturnIfAbrupt(P). + // no throw exception, so needn't return + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 6. If Type(P) is not Object, throw a TypeError exception. + if (!constructorPrototype->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "HasInstance: is not Object", false); + } + + // 7. Repeat + // a.Let O be O.[[GetPrototypeOf]](). + // b.ReturnIfAbrupt(O). + // c.If O is null, return false. + // d.If SameValue(P, O) is true, return true. + JSTaggedValue objPrototype = obj.GetTaggedValue(); + while (!objPrototype.IsNull()) { + if (JSTaggedValue::SameValue(objPrototype, constructorPrototype.GetTaggedValue())) { + return true; + } + objPrototype = JSObject::Cast(objPrototype)->GetPrototype(thread); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + } + return false; +} + +bool JSFunction::MakeConstructor(JSThread *thread, const JSHandle &func, + const JSHandle &proto, bool writable) +{ + ASSERT_PRINT(proto->IsHeapObject() || proto->IsUndefined(), "proto must be JSObject or Undefined"); + ASSERT_PRINT(func->IsConstructor(), "func must be Constructor type"); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + + ASSERT_PRINT(func->GetProtoOrDynClass().IsHole() && func->IsExtensible(), + "function doesn't has proto_type property and is extensible object"); + ASSERT_PRINT(JSObject::HasProperty(thread, JSHandle(func), constructorKey), + "function must have constructor"); + + // proto.constructor = func + bool status = false; + if (proto->IsUndefined()) { + // Let prototype be ObjectCreate(%ObjectPrototype%). + JSHandle objPrototype = env->GetObjectFunctionPrototype(); + PropertyDescriptor constructorDesc(thread, JSHandle::Cast(func), writable, false, true); + status = JSTaggedValue::DefinePropertyOrThrow(thread, objPrototype, constructorKey, constructorDesc); + } else { + PropertyDescriptor constructorDesc(thread, JSHandle::Cast(func), writable, false, true); + status = JSTaggedValue::DefinePropertyOrThrow(thread, proto, constructorKey, constructorDesc); + } + + ASSERT_PRINT(status, "DefineProperty construct failed"); + // func.prototype = proto + // Let status be DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: + // prototype, [[Writable]]: writablePrototype, [[Enumerable]]: false, [[Configurable]]: false}). + func->SetFunctionPrototype(thread, proto.GetTaggedValue()); + + ASSERT_PRINT(status, "DefineProperty proto_type failed"); + return status; +} + +// set property constructor and prototype for class +bool JSFunction::MakeClassConstructor(JSThread *thread, const JSHandle &cls, + const JSHandle &clsPrototype) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + bool status = true; + PropertyDescriptor ctorDesc(thread, cls, true, false, true); + status = status && JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle::Cast(clsPrototype), + globalConst->GetHandledConstructorString(), ctorDesc); + + JSHandle::Cast(cls)->SetFunctionPrototype(thread, clsPrototype.GetTaggedValue()); + JSHandle::Cast(cls)->SetHomeObject(thread, clsPrototype.GetTaggedValue()); + return status; +} + +JSTaggedValue JSFunction::Call(JSThread *thread, const JSHandle &func, + const JSHandle &thisArg, const JSHandle &argv) +{ + // 1. ReturnIfAbrupt(F). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 2. If argumentsList was not passed, let argumentsList be a new empty List. + // 3. If IsCallable(F) is false, throw a TypeError exception. + if (!func->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception()); + } + if (func->IsJSFunction()) { + return JSFunction::CallInternal(thread, JSHandle(func), thisArg, argv); + } + // 4. Return F.[[Call]](V, argumentsList). + if (func->IsBoundFunction()) { + return JSBoundFunction::CallInternal(thread, JSHandle(func), thisArg, argv); + } + if (func->IsJSProxy()) { + return JSProxy::CallInternal(thread, JSHandle(func), thisArg, argv); + } + + THROW_TYPE_ERROR_AND_RETURN(thread, "Call NonCallable", JSTaggedValue::Exception()); +} + +JSTaggedValue JSFunction::Construct(JSThread *thread, const JSHandle &func, + const JSHandle &argv, const JSHandle &newTarget) +{ + JSMutableHandle target(thread, newTarget.GetTaggedValue()); + if (target->IsUndefined()) { + target.Update(func.GetTaggedValue()); + } + if (!(func->IsConstructor() && target->IsConstructor())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception()); + } + if (func->IsJSFunction()) { + return JSFunction::ConstructInternal(thread, JSHandle(func), argv, target); + } + if (func->IsBoundFunction()) { + return JSBoundFunction::ConstructInternal(thread, JSHandle(func), argv, target); + } + if (func->IsJSProxy()) { + return JSProxy::ConstructInternal(thread, JSHandle(func), argv, target); + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor NonConstructor", JSTaggedValue::Exception()); +} + +JSTaggedValue JSFunction::Invoke(JSThread *thread, const JSHandle &thisArg, + const JSHandle &key, const JSHandle &argv) +{ + ASSERT(JSTaggedValue::IsPropertyKey(key)); + JSHandle func(JSTaggedValue::GetProperty(thread, thisArg, key).GetValue()); + return JSFunction::Call(thread, func, thisArg, argv); +} + +// [[Call]] +JSTaggedValue JSFunction::CallInternal(JSThread *thread, const JSHandle &func, + const JSHandle &thisArg, const JSHandle &argv) +{ + if (!func->IsBuiltinsConstructor() && func->IsClassConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "class constructor cannot call", JSTaggedValue::Exception()); + } + CallParams params; + params.callTarget = ECMAObject::Cast(*func); + params.newTarget = JSTaggedValue::VALUE_UNDEFINED; + params.thisArg = thisArg.GetTaggedType(); + params.argc = argv->GetLength(); + CVector values; + values.reserve(params.argc); + + for (array_size_t i = 0; i < params.argc; ++i) { + JSTaggedValue arg = argv->Get(i); + values.emplace_back(arg.GetRawData()); + } + params.argv = values.data(); + return EcmaInterpreter::Execute(thread, params); +} + +// [[Construct]] +JSTaggedValue JSFunction::ConstructInternal(JSThread *thread, const JSHandle &func, + const JSHandle &argv, const JSHandle &newTarget) +{ + ASSERT(newTarget->IsECMAObject()); + if (!func->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception()); + } + + JSHandle obj(thread, JSTaggedValue::Undefined()); + if (!func->IsBuiltinsConstructor() && func->IsBase()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + obj = JSHandle(factory->NewJSObjectByConstructor(func, newTarget)); + } + + CallParams params; + params.callTarget = ECMAObject::Cast(*func); + params.newTarget = newTarget.GetTaggedType(); + params.thisArg = obj.GetTaggedType(); + params.argc = argv->GetLength(); + CVector values; + values.reserve(params.argc); + + for (uint32_t i = 0; i < params.argc; ++i) { + JSTaggedValue arg = argv->Get(i); + values.emplace_back(arg.GetRawData()); + } + params.argv = values.data(); + + JSTaggedValue resultValue = EcmaInterpreter::Execute(thread, params); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 9.3.2 [[Construct]] (argumentsList, newTarget) + if (func->IsBuiltinsConstructor() || resultValue.IsECMAObject()) { + return resultValue; + } + + if (func->IsBase()) { + return obj.GetTaggedValue(); + } + + // derived ctor(sub class) return the obj which created by base ctor(parent class) + if (func->IsDerivedConstructor()) { + return resultValue; + } + + if (!resultValue.IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "function is non-constructor", JSTaggedValue::Exception()); + } + return obj.GetTaggedValue(); +} + +void JSFunction::MakeMethod(const JSThread *thread, const JSHandle &func, + const JSHandle &homeObject) +{ + func->SetHomeObject(thread, homeObject); +} + +JSHandle JSFunctionBase::GetFunctionName(JSThread *thread, const JSHandle &func) +{ + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + return JSObject::GetProperty(thread, JSHandle(func), nameKey).GetValue(); +} + +bool JSFunctionBase::SetFunctionName(JSThread *thread, const JSHandle &func, + const JSHandle &name, const JSHandle &prefix) +{ + ASSERT_PRINT(func->IsExtensible(), "Function must be extensible"); + ASSERT_PRINT(name->IsStringOrSymbol(), "name must be string or symbol"); + bool needPrefix = false; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (!prefix->IsUndefined()) { + ASSERT_PRINT(prefix->IsString(), "prefix must be string"); + needPrefix = true; + } + // If Type(name) is Symbol, then + // Let description be name’s [[Description]] value. + // If description is undefined, let name be the empty String. + // Else, let name be the concatenation of "[", description, and "]". + JSHandle functionName; + if (name->IsSymbol()) { + JSTaggedValue description = JSHandle::Cast(name)->GetDescription(); + JSHandle descriptionHandle(thread, description); + if (description.IsUndefined()) { + functionName = factory->GetEmptyString(); + } else { + JSHandle leftBrackets = factory->NewFromString("["); + JSHandle rightBrackets = factory->NewFromString("]"); + functionName = factory->ConcatFromString(leftBrackets, descriptionHandle); + functionName = factory->ConcatFromString(functionName, rightBrackets); + } + } else { + functionName = JSHandle::Cast(name); + } + EcmaString *newString; + if (needPrefix) { + JSHandle handlePrefixString = JSTaggedValue::ToString(thread, prefix); + JSHandle spaceString(factory->NewFromString(" ")); + JSHandle concatString = factory->ConcatFromString(handlePrefixString, spaceString); + newString = *factory->ConcatFromString(concatString, functionName); + } else { + newString = *functionName; + } + JSHandle nameHandle(thread, newString); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + PropertyDescriptor nameDesc(thread, nameHandle, false, false, true); + JSHandle funcHandle(func); + return JSTaggedValue::DefinePropertyOrThrow(thread, funcHandle, nameKey, nameDesc); +} + +bool JSFunction::SetFunctionLength(JSThread *thread, const JSHandle &func, JSTaggedValue length, bool cfg) +{ + ASSERT_PRINT(func->IsExtensible(), "Function must be extensible"); + ASSERT_PRINT(length.IsInteger(), "length must be integer"); + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + ASSERT_PRINT(!JSTaggedValue::Less(thread, JSHandle(thread, length), + JSHandle(thread, JSTaggedValue(0))), + "length must be non negtive integer"); + PropertyDescriptor lengthDesc(thread, JSHandle(thread, length), false, false, cfg); + JSHandle funcHandle(func); + return JSTaggedValue::DefinePropertyOrThrow(thread, funcHandle, lengthKeyHandle, lengthDesc); +} + +// 9.4.1.1[[Call]](thisArgument, argumentsList) +JSTaggedValue JSBoundFunction::CallInternal(JSThread *thread, const JSHandle &func, + [[maybe_unused]] const JSHandle &thisArg, + const JSHandle &argv) +{ + JSHandle target(thread, func->GetBoundTarget()); + JSHandle boundThis(thread, func->GetBoundThis()); + JSHandle boundArgs(thread, func->GetBoundArguments()); + JSHandle arguments = TaggedArray::Append(thread, boundArgs, argv); + return JSFunction::Call(thread, target, boundThis, arguments); +} + +// 9.4.1.2[[Construct]](argumentsList, newTarget) +JSTaggedValue JSBoundFunction::ConstructInternal(JSThread *thread, const JSHandle &func, + const JSHandle &argv, + const JSHandle &newTarget) +{ + JSHandle target(thread, func->GetBoundTarget()); + ASSERT(target->IsConstructor()); + JSHandle boundArgs(thread, func->GetBoundArguments()); + JSHandle arguments = TaggedArray::Append(thread, boundArgs, argv); + JSMutableHandle newTargetMutable(thread, newTarget.GetTaggedValue()); + if (JSTaggedValue::SameValue(func.GetTaggedValue(), newTarget.GetTaggedValue())) { + newTargetMutable.Update(target.GetTaggedValue()); + } + return JSFunction::Construct(thread, target, arguments, newTargetMutable); +} + +void JSProxyRevocFunction::ProxyRevocFunctions(const JSThread *thread, const JSHandle &revoker) +{ + // 1.Let p be the value of F’s [[RevocableProxy]] internal slot. + JSTaggedValue proxy = revoker->GetRevocableProxy(); + // 2.If p is null, return undefined. + if (proxy.IsNull()) { + return; + } + + // 3.Set the value of F’s [[RevocableProxy]] internal slot to null. + revoker->SetRevocableProxy(thread, JSTaggedValue::Null()); + + // 4.Assert: p is a Proxy object. + ASSERT(proxy.IsJSProxy()); + JSHandle proxyHandle(thread, proxy); + + // 5 ~ 6 Set internal slot of p to null. + proxyHandle->SetTarget(thread, JSTaggedValue::Null()); + proxyHandle->SetHandler(thread, JSTaggedValue::Null()); +} + +JSTaggedValue JSFunction::AccessCallerArgumentsThrowTypeError([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), + "Under strict mode, 'caller' and 'arguments' properties must not be accessed.", + JSTaggedValue::Exception()); +} + +void JSFunction::SetFunctionNameNoPrefix(JSThread *thread, JSFunction *func, JSTaggedValue name) +{ + ASSERT_PRINT(func->IsExtensible(), "Function must be extensible"); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle funcHandle(thread, func); + { + JSMutableHandle nameHandle(thread, JSTaggedValue::Undefined()); + if (!name.IsSymbol()) { + nameHandle.Update(name); + } else { + JSHandle nameBegin(thread, name); + JSTaggedValue description = JSSymbol::Cast(name.GetTaggedObject())->GetDescription(); + if (description.IsUndefined()) { + nameHandle.Update(thread->GlobalConstants()->GetEmptyString()); + } else { + JSHandle concatName; + JSHandle leftBrackets = factory->NewFromString("["); + JSHandle rightBrackets = factory->NewFromString("]"); + concatName = factory->ConcatFromString( + leftBrackets, + JSHandle(thread, JSSymbol::Cast(nameBegin->GetHeapObject())->GetDescription())); + concatName = factory->ConcatFromString(concatName, rightBrackets); + nameHandle.Update(concatName.GetTaggedValue()); + } + } + PropertyDescriptor nameDesc(thread, nameHandle, false, false, true); + JSTaggedValue::DefinePropertyOrThrow(thread, funcHandle, thread->GlobalConstants()->GetHandledNameString(), + nameDesc); + } +} + +JSHandle JSFunction::GetInstanceJSHClass(JSThread *thread, JSHandle constructor, + JSHandle newTarget) +{ + JSHandle ctorInitialJSHClass(thread, JSFunction::GetOrCreateInitialJSHClass(thread, constructor)); + // newTarget is construct itself + if (newTarget.GetTaggedValue() == constructor.GetTaggedValue()) { + return ctorInitialJSHClass; + } + + // newTarget is derived-class of constructor + if (newTarget->IsJSFunction()) { + JSHandle newTargetFunc = JSHandle::Cast(newTarget); + if (newTargetFunc->IsDerivedConstructor()) { + JSTaggedValue newTargetProto = JSHandle::Cast(newTarget)->GetPrototype(thread); + if (newTargetProto == constructor.GetTaggedValue()) { + return GetOrCreateDerivedJSHClass(thread, newTargetFunc, constructor, ctorInitialJSHClass); + } + } + } + + // ECMA2015 9.1.15 3.Let proto be Get(constructor, "prototype"). + JSMutableHandle prototype(thread, JSTaggedValue::Undefined()); + if (newTarget->IsJSFunction()) { + JSHandle newTargetFunc = JSHandle::Cast(newTarget); + FunctionKind kind = newTargetFunc->GetFunctionKind(); + if (HasPrototype(kind)) { + prototype.Update(PrototypeGetter(thread, JSHandle::Cast(newTargetFunc))); + } + } else { + // Such case: bound function and define a "prototype" property. + JSHandle customizePrototype = + JSTaggedValue::GetProperty(thread, newTarget, thread->GlobalConstants()->GetHandledPrototypeString()) + .GetValue(); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSHClass, thread); + prototype.Update(customizePrototype.GetTaggedValue()); + // Reload JSHClass of constructor, where the lookup of 'prototype' property may change it. + ctorInitialJSHClass = JSHandle(thread, JSFunction::GetOrCreateInitialJSHClass(thread, constructor)); + } + + if (!prototype->IsECMAObject()) { + prototype.Update(constructor->GetFunctionPrototype()); + } + + JSHandle newJSHClass = JSHClass::Clone(thread, ctorInitialJSHClass); + newJSHClass->SetPrototype(thread, prototype); + + return newJSHClass; +} + +JSHandle JSFunction::GetOrCreateDerivedJSHClass(JSThread *thread, JSHandle derived, + JSHandle constructor, + JSHandle ctorInitialJSHClass) +{ + JSTaggedValue protoOrDyn(derived->GetProtoOrDynClass()); + // has cached JSHClass, return directly + if (protoOrDyn.IsJSHClass()) { + return JSHandle(thread, protoOrDyn); + } + + JSHandle newJSHClass = JSHClass::Clone(thread, ctorInitialJSHClass); + // guarante derived has function prototype + JSHandle prototype(thread, derived->GetProtoOrDynClass()); + newJSHClass->SetPrototype(thread, prototype); + derived->SetProtoOrDynClass(thread, newJSHClass); + return newJSHClass; +} + +// Those interface below is discarded +void JSFunction::InitializeJSFunction(JSThread *thread, [[maybe_unused]] const JSHandle &env, + const JSHandle &func, FunctionKind kind, bool strict) +{ + InitializeJSFunction(thread, func, kind, strict); +} + +bool JSFunction::IsDynClass(JSTaggedValue object) +{ + return object.IsJSHClass(); +} + +DynClass *JSFunction::GetOrCreateInitialDynClass(JSThread *thread, const JSHandle &fun) +{ + return reinterpret_cast(JSFunction::GetOrCreateInitialJSHClass(thread, fun)); +} + +JSHandle JSFunction::GetInstanceDynClass(JSThread *thread, JSHandle constructor, + JSHandle newTarget) +{ + return JSHandle(JSFunction::GetInstanceJSHClass(thread, constructor, newTarget)); +} + +bool JSFunction::NameSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + [[maybe_unused]] bool mayThrow) +{ + if (self->IsPropertiesDict()) { + // replace setter with value + JSHandle nameString = thread->GlobalConstants()->GetHandledNameString(); + return self->UpdatePropertyInDictionary(thread, nameString.GetTaggedValue(), value.GetTaggedValue()); + } + self->SetPropertyInlinedProps(thread, NAME_INLINE_PROPERTY_INDEX, value.GetTaggedValue()); + return true; +} + +bool JSFunction::LengthSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + [[maybe_unused]] bool mayThrow) +{ + if (self->IsPropertiesDict()) { + // replace setter with value + JSHandle lengthString = thread->GlobalConstants()->GetHandledLengthString(); + return self->UpdatePropertyInDictionary(thread, lengthString.GetTaggedValue(), value.GetTaggedValue()); + } + self->SetPropertyInlinedProps(thread, LENGTH_INLINE_PROPERTY_INDEX, value.GetTaggedValue()); + return true; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_function.h b/ecmascript/js_function.h new file mode 100644 index 0000000000000000000000000000000000000000..f13b07ca9f66f408e2f971c793345371b9ffc6f5 --- /dev/null +++ b/ecmascript/js_function.h @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSFUCNTION_H +#define PANDA_RUNTIME_ECMASCRIPT_JSFUCNTION_H + +#include "ecmascript/accessor_data.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/js_function_extra_info.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/lexical_env.h" + +namespace panda::ecmascript { +using panda::coretypes::DynClass; +class JSThread; + +class JSFunctionBase : public JSObject { +public: + static JSFunctionBase *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSFunctionBase()); + return static_cast(object); + } + + static const JSFunctionBase *ConstCast(const ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSFunctionBase()); + return static_cast(object); + } + + inline void SetConstructor(bool flag) + { + JSHClass *hclass = GetJSHClass(); + hclass->SetConstructor(flag); + } + + static bool SetFunctionName(JSThread *thread, const JSHandle &func, + const JSHandle &name, const JSHandle &prefix); + static JSHandle GetFunctionName(JSThread *thread, const JSHandle &func); + + void SetCallTarget([[maybe_unused]] const JSThread *thread, JSMethod *p) + { + SetMethod(p); + } + + static constexpr size_t METHOD_OFFSET = JSObject::SIZE; + SET_GET_NATIVE_FIELD(Method, JSMethod, METHOD_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, SIZE, SIZE) +}; + +class JSFunction : public JSFunctionBase { +public: + static constexpr int LENGTH_OF_INLINE_PROPERTIES = 3; + static constexpr int LENGTH_INLINE_PROPERTY_INDEX = 0; + static constexpr int NAME_INLINE_PROPERTY_INDEX = 1; + static constexpr int PROTOTYPE_INLINE_PROPERTY_INDEX = 2; + static constexpr int CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX = 1; + + /* -------------- Common API Begin, Don't change those interface!!! ----------------- */ + static JSFunction *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSFunction()); + return static_cast(object); + } + + static void InitializeJSFunction(JSThread *thread, const JSHandle &env, const JSHandle &func, + FunctionKind kind = FunctionKind::NORMAL_FUNCTION, bool strict = true); + // ecma6 7.3 + static bool OrdinaryHasInstance(JSThread *thread, const JSHandle &constructor, + const JSHandle &obj); + + static JSTaggedValue SpeciesConstructor(const JSHandle &func, + const JSHandle &defaultConstructor); + + // ecma6 9.2 + // 7.3.12 Call(F, V, argumentsList) + static JSTaggedValue Call(JSThread *thread, const JSHandle &func, + const JSHandle &thisArg, const JSHandle &argv); + static JSTaggedValue Construct(JSThread *thread, const JSHandle &func, + const JSHandle &argv, const JSHandle &newTarget); + static JSTaggedValue Invoke(JSThread *thread, const JSHandle &thisArg, + const JSHandle &key, const JSHandle &argv); + // 9.2.1[[Call]](thisArgument, argumentsList) + // 9.3.1[[Call]](thisArgument, argumentsList) + static JSTaggedValue CallInternal(JSThread *thread, const JSHandle &func, + const JSHandle &thisArg, const JSHandle &argv); + // 9.2.2[[Construct]](argumentsList, newTarget) + // 9.3.2[[Construct]](argumentsList, newTarget) + static JSTaggedValue ConstructInternal(JSThread *thread, const JSHandle &func, + const JSHandle &argv, const JSHandle &newTarget); + + static bool AddRestrictedFunctionProperties(const JSHandle &func, const JSHandle &realm); + static bool MakeConstructor(JSThread *thread, const JSHandle &func, + const JSHandle &proto, bool writable = true); + static bool MakeClassConstructor(JSThread *thread, const JSHandle &cls, + const JSHandle &clsPrototype); + static void MakeMethod(const JSThread *thread, const JSHandle &func, + const JSHandle &homeObject); + static bool SetFunctionLength(JSThread *thread, const JSHandle &func, JSTaggedValue length, + bool cfg = true); + static JSHandle NewJSFunctionPrototype(JSThread *thread, ObjectFactory *factory, + const JSHandle &func); + static DynClass *GetOrCreateInitialDynClass(JSThread *thread, const JSHandle &fun); + static JSTaggedValue AccessCallerArgumentsThrowTypeError(EcmaRuntimeCallInfo *argv); + static bool IsDynClass(JSTaggedValue object); + static JSTaggedValue PrototypeGetter(JSThread *thread, const JSHandle &self); + static bool PrototypeSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + bool mayThrow); + static JSTaggedValue NameGetter(JSThread *thread, const JSHandle &self); + static bool NameSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + bool mayThrow); + static JSTaggedValue LengthGetter(JSThread *thread, const JSHandle &self); + static bool LengthSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + bool mayThrow); + static void SetFunctionNameNoPrefix(JSThread *thread, JSFunction *func, JSTaggedValue name); + static JSHandle GetInstanceDynClass(JSThread *thread, JSHandle constructor, + JSHandle newTarget); + + inline JSTaggedValue GetFunctionPrototype() const + { + ASSERT(HasFunctionPrototype()); + JSTaggedValue protoOrDyn = GetProtoOrDynClass(); + if (protoOrDyn.IsJSHClass()) { + return JSHClass::Cast(protoOrDyn.GetTaggedObject())->GetPrototype(); + } + + return protoOrDyn; + } + + inline void SetFunctionPrototype(const JSThread *thread, JSTaggedValue proto) + { + SetProtoOrDynClass(thread, proto); + if (proto.IsJSHClass()) { + proto = JSHClass::Cast(proto.GetTaggedObject())->GetPrototype(); + } + if (proto.IsECMAObject()) { + proto.GetTaggedObject()->GetClass()->SetIsPrototype(true); + } + } + + inline bool HasInitialDynClass() const + { + JSTaggedValue protoOrDyn = GetProtoOrDynClass(); + return protoOrDyn.IsJSHClass(); + } + + inline bool HasFunctionPrototype() const + { + JSTaggedValue protoOrDyn = GetProtoOrDynClass(); + return !protoOrDyn.IsHole(); + } + + inline DynClass *GetInitialDynClass() const + { + ASSERT(HasInitialDynClass()); + JSTaggedValue protoOrDyn = GetProtoOrDynClass(); + return reinterpret_cast(protoOrDyn.GetTaggedObject()); + } + + inline void SetFunctionLength(const JSThread *thread, JSTaggedValue length) + { + ASSERT(!IsPropertiesDict()); + SetPropertyInlinedProps(thread, LENGTH_INLINE_PROPERTY_INDEX, length); + } + + inline bool IsBase() const + { + FunctionKind kind = GetFunctionKind(); + return kind <= FunctionKind::CLASS_CONSTRUCTOR; + } + + inline bool IsDerivedConstructor() const + { + FunctionKind kind = GetFunctionKind(); + return kind == FunctionKind::DERIVED_CONSTRUCTOR || kind == FunctionKind::DEFAULT_DERIVED_CONSTRUCTOR; + } + + inline void SetFunctionKind(const JSThread *thread, FunctionKind kind) + { + JSTaggedType oldValue = GetFunctionInfoFlag().GetRawData(); + SetFunctionInfoFlag(thread, JSTaggedValue(FunctionKindBit::Update(oldValue, kind))); + } + + inline void SetStrict(const JSThread *thread, bool flag) + { + JSTaggedType oldValue = GetFunctionInfoFlag().GetRawData(); + SetFunctionInfoFlag(thread, JSTaggedValue(StrictBit::Update(oldValue, flag))); + } + + inline void SetClassConstructor(const JSThread *thread, bool flag) + { + JSTaggedType oldValue = GetFunctionInfoFlag().GetRawData(); + SetFunctionInfoFlag(thread, JSTaggedValue(ClassConstructorBit::Update(oldValue, flag))); + } + + inline void SetResolved(const JSThread *thread) + { + TaggedType oldValue = GetFunctionInfoFlag().GetRawData(); + SetFunctionInfoFlag(thread, JSTaggedValue(ResolvedBit::Update(oldValue, true))); + } + + inline bool IsResolved() const + { + return ResolvedBit::Decode(GetFunctionInfoFlag().GetInt()); + } + + inline void SetFunctionMode(const JSThread *thread, FunctionMode mode) + { + JSTaggedType oldValue = GetFunctionInfoFlag().GetRawData(); + SetFunctionInfoFlag(thread, JSTaggedValue(ThisModeBit::Update(oldValue, mode))); + } + + inline FunctionKind GetFunctionKind() const + { + return FunctionKindBit::Decode(GetFunctionInfoFlag().GetInt()); + } + + inline bool IsStrict() const + { + return StrictBit::Decode(GetFunctionInfoFlag().GetInt()); + } + + inline bool IsClassConstructor() const + { + return ClassConstructorBit::Decode(GetFunctionInfoFlag().GetInt()); + } + + inline FunctionMode GetFunctionMode() const + { + return ThisModeBit::Decode(GetFunctionInfoFlag().GetInt()); + } + + inline static bool IsArrowFunction(FunctionKind kind) + { + return (kind >= ARROW_FUNCTION) && (kind <= ASYNC_ARROW_FUNCTION); + } + + inline static bool IsClassConstructor(FunctionKind kind) + { + return (kind >= CLASS_CONSTRUCTOR) && (kind <= DERIVED_CONSTRUCTOR); + } + + inline static bool IsConstructorKind(FunctionKind kind) + { + return (kind >= BUILTIN_PROXY_CONSTRUCTOR) && (kind <= DERIVED_CONSTRUCTOR); + } + + inline static bool IsBuiltinConstructor(FunctionKind kind) + { + return kind >= BUILTIN_PROXY_CONSTRUCTOR && kind <= BUILTIN_CONSTRUCTOR; + } + + inline static bool HasPrototype(FunctionKind kind) + { + return kind >= BUILTIN_CONSTRUCTOR && kind <= GENERATOR_FUNCTION; + } + /* -------------- Common API End, Don't change those interface!!! ----------------- */ + static void InitializeJSFunction(JSThread *thread, const JSHandle &func, + FunctionKind kind = FunctionKind::NORMAL_FUNCTION, bool strict = true); + static JSHClass *GetOrCreateInitialJSHClass(JSThread *thread, const JSHandle &fun); + static JSHandle GetInstanceJSHClass(JSThread *thread, JSHandle constructor, + JSHandle newTarget); + + static constexpr size_t PROTO_OR_DYNCLASS_OFFSET = JSFunctionBase::SIZE; + ACCESSORS(ProtoOrDynClass, PROTO_OR_DYNCLASS_OFFSET, LEXICAL_ENV_OFFSET) + ACCESSORS(LexicalEnv, LEXICAL_ENV_OFFSET, HOME_OBJECT_OFFSET) + ACCESSORS(HomeObject, HOME_OBJECT_OFFSET, FUNCTION_INFO_FLAG_OFFSET) + ACCESSORS(FunctionInfoFlag, FUNCTION_INFO_FLAG_OFFSET, FUNCTION_EXTRA_INFO_OFFSET) + ACCESSORS(FunctionExtraInfo, FUNCTION_EXTRA_INFO_OFFSET, CONSTANT_POOL_OFFSET) + ACCESSORS(ConstantPool, CONSTANT_POOL_OFFSET, PROFILE_TYPE_INFO_OFFSET) + ACCESSORS(ProfileTypeInfo, PROFILE_TYPE_INFO_OFFSET, SIZE) + + static constexpr uint32_t FUNCTION_KIND_BIT_NUM = 5; + using FunctionKindBit = BitField; + using StrictBit = FunctionKindBit::NextFlag; + using ClassConstructorBit = StrictBit::NextFlag; + + using ResolvedBit = ClassConstructorBit::NextFlag; + using ThisModeBit = ResolvedBit::NextField; // 2: means this flag occupies two digits. + + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunctionBase, PROTO_OR_DYNCLASS_OFFSET, SIZE) + +private: + static JSHandle GetOrCreateDerivedJSHClass(JSThread *thread, JSHandle derived, + JSHandle constructor, + JSHandle ctorInitialDynClass); +}; + +class JSGeneratorFunction : public JSFunction { +public: + static JSGeneratorFunction *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsGeneratorFunction()); + return static_cast(object); + } + + static constexpr size_t SIZE = JSFunction::SIZE; + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, SIZE, SIZE) + + DECL_DUMP() +}; + +class JSBoundFunction : public JSFunctionBase { +public: + static JSBoundFunction *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsBoundFunction()); + return static_cast(object); + } + + // 9.4.1.1[[Call]](thisArgument, argumentsList) + static JSTaggedValue CallInternal(JSThread *thread, const JSHandle &func, + const JSHandle &thisArg, const JSHandle &argv); + // 9.4.1.2[[Construct]](argumentsList, newTarget) + static JSTaggedValue ConstructInternal(JSThread *thread, const JSHandle &func, + const JSHandle &argv, const JSHandle &newTarget); + + static constexpr size_t BOUND_TARGET_OFFSET = JSFunctionBase::SIZE; + ACCESSORS(BoundTarget, BOUND_TARGET_OFFSET, BOUND_THIS_OFFSET); + ACCESSORS(BoundThis, BOUND_THIS_OFFSET, BOUND_ARGUMENTS_OFFSET); + ACCESSORS(BoundArguments, BOUND_ARGUMENTS_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunctionBase, BOUND_TARGET_OFFSET, SIZE) + + DECL_DUMP() +}; + +class JSProxyRevocFunction : public JSFunction { +public: + static JSProxyRevocFunction *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsProxyRevocFunction()); + return static_cast(object); + } + + static void ProxyRevocFunctions(const JSThread *thread, const JSHandle &revoker); + + static constexpr size_t REVOCABLE_PROXY_OFFSET = JSFunction::SIZE; + ACCESSORS(RevocableProxy, REVOCABLE_PROXY_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, REVOCABLE_PROXY_OFFSET, SIZE) + + DECL_DUMP() +}; + +// ResolveFunction/RejectFunction +class JSPromiseReactionsFunction : public JSFunction { +public: + static JSPromiseReactionsFunction *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSPromiseReactionFunction()); + return static_cast(object); + } + + static constexpr size_t PROMISE_OFFSET = JSFunction::SIZE; + ACCESSORS(Promise, PROMISE_OFFSET, ALREADY_RESOLVED_OFFSET); + ACCESSORS(AlreadyResolved, ALREADY_RESOLVED_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, PROMISE_OFFSET, SIZE) + + DECL_DUMP() +}; + +// ExecutorFunction +class JSPromiseExecutorFunction : public JSFunction { +public: + static JSPromiseExecutorFunction *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSPromiseExecutorFunction()); + return static_cast(object); + } + + static constexpr size_t CAPABILITY_OFFSET = JSFunction::SIZE; + ACCESSORS(Capability, CAPABILITY_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, CAPABILITY_OFFSET, SIZE) + + DECL_DUMP() +}; + +class JSPromiseAllResolveElementFunction : public JSFunction { +public: + static JSPromiseAllResolveElementFunction *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSPromiseAllResolveElementFunction()); + return static_cast(object); + } + + static constexpr size_t INDEX_OFFSET = JSFunction::SIZE; + ACCESSORS(Index, INDEX_OFFSET, VALUES_OFFSET); + ACCESSORS(Values, VALUES_OFFSET, CAPABILITIES_OFFSET); + ACCESSORS(Capabilities, CAPABILITIES_OFFSET, REMAINING_ELEMENTS_OFFSET); + ACCESSORS(RemainingElements, REMAINING_ELEMENTS_OFFSET, ALREADY_CALLED_OFFSET); + ACCESSORS(AlreadyCalled, ALREADY_CALLED_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, INDEX_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSFUCNTION_H diff --git a/ecmascript/js_function_extra_info.h b/ecmascript/js_function_extra_info.h new file mode 100644 index 0000000000000000000000000000000000000000..1258e4450dd5f1c879899c5ec4f02e047836eac0 --- /dev/null +++ b/ecmascript/js_function_extra_info.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_FUNCTION_INFO_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_FUNCTION_INFO_H + +#include "utils/bit_field.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_string.h" +#include "js_tagged_value-inl.h" +#include "js_function_kind.h" + +namespace panda::ecmascript { +class JSFunctionExtraInfo : public TaggedObject { +public: + static JSFunctionExtraInfo *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSFunctionExtraInfo()); + return static_cast(object); + } + + static constexpr size_t CALL_BACK_OFFSET = sizeof(TaggedObject); + ACCESSORS(Callback, CALL_BACK_OFFSET, DATA_OFFSET); + ACCESSORS(Data, DATA_OFFSET, SIZE); + + DECL_VISIT_OBJECT(CALL_BACK_OFFSET, SIZE) +}; +} // namespace panda::ecmascript +#endif diff --git a/ecmascript/js_function_kind.h b/ecmascript/js_function_kind.h new file mode 100644 index 0000000000000000000000000000000000000000..d035caf509f12200f61d174274060fe244201334 --- /dev/null +++ b/ecmascript/js_function_kind.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_FUNCTION_KIND_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_FUNCTION_KIND_H + +#include + +namespace panda::ecmascript { +enum FunctionKind : uint8_t { + // BEGIN constructable functions + NORMAL_FUNCTION = 0, + BUILTIN_PROXY_CONSTRUCTOR, + BUILTIN_CONSTRUCTOR, + // BEGIN class constructors + // BEGIN base constructors + BASE_CONSTRUCTOR, + // BEGIN default constructors + DEFAULT_BASE_CONSTRUCTOR, + CLASS_CONSTRUCTOR, + // END base constructors + // BEGIN derived constructors + DEFAULT_DERIVED_CONSTRUCTOR, + // END default constructors + DERIVED_CONSTRUCTOR, + // END derived constructors + // END class constructors + // END constructable functions. + GENERATOR_FUNCTION, + // END generators + + // BEGIN accessors + GETTER_FUNCTION, + SETTER_FUNCTION, + // END accessors + // BEGIN arrow functions + ARROW_FUNCTION, + // BEGIN async functions + ASYNC_ARROW_FUNCTION, + // END arrow functions + ASYNC_FUNCTION, + // BEGIN concise methods 1 + ASYNC_CONCISE_METHOD, + // BEGIN generators + ASYNC_CONCISE_GENERATOR_METHOD, + // END concise methods 1 + ASYNC_GENERATOR_FUNCTION, + // END async functions + CONSIZE_METHDOD, + + CLASS_MEMBERS_INITIALIZER_FUNCTION, + // END concise methods 2 + + LAST_FUNCTION_KIND = CLASS_MEMBERS_INITIALIZER_FUNCTION, +}; + +enum FunctionMode : uint8_t { + LEXICAL, + STRICT, + GLOBAL, +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_FUNCTION_KIND_H diff --git a/ecmascript/js_generator_object.cpp b/ecmascript/js_generator_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e835b0f9a03b0397207f3ea702e43d485beb16b5 --- /dev/null +++ b/ecmascript/js_generator_object.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_generator_object.h" +#include "generator_helper.h" +#include "js_iterator.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +JSTaggedValue JSGeneratorObject::GeneratorValidate(JSThread *thread, const JSHandle &obj) +{ + // 1.Perform ? RequireInternalSlot(generator, [[GeneratorState]]). + // 2.Assert: generator also has a [[GeneratorContext]] internal slot. + if (!obj->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", JSTaggedValue::Undefined()); + } + JSHandle toObj = JSTaggedValue::ToObject(thread, obj); + if (!toObj->IsGeneratorObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", JSTaggedValue::Undefined()); + } + + // 3.Let state be generator.[[GeneratorState]]. + JSHandle generator(thread, JSGeneratorObject::Cast(*(toObj))); + JSTaggedValue state = generator->GetGeneratorState(); + // 4.If state is executing, throw a TypeError exception. + if (state == JSTaggedValue(static_cast(JSGeneratorState::EXECUTING))) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", JSTaggedValue::Undefined()); + } + // 5.Return state. + return state; +} + +JSHandle JSGeneratorObject::GeneratorResume(JSThread *thread, const JSHandle &generator, + JSTaggedValue value) +{ + // 1.Let state be ? GeneratorValidate(generator). + JSHandle gen(thread, generator.GetTaggedValue()); + JSTaggedValue state = GeneratorValidate(thread, gen); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + + // 2.If state is completed, return CreateIterResultObject(undefined, true). + if (state == JSTaggedValue(static_cast(JSGeneratorState::COMPLETED))) { + JSHandle valueHandle(thread, JSTaggedValue::Undefined()); + return JSIterator::CreateIterResultObject(thread, valueHandle, true); + } + + // 3.Assert: state is either suspendedStart or suspendedYield. + ASSERT_PRINT(state == JSTaggedValue(static_cast(JSGeneratorState::SUSPENDED_START)) || + state == JSTaggedValue(static_cast(JSGeneratorState::SUSPENDED_YIELD)), + "state is neither suspendedStart nor suspendedYield"); + + // 4.Let genContext be generator.[[GeneratorContext]]. + JSHandle genContext(thread, generator->GetGeneratorContext()); + + // 5.Let methodContext be the running execution context. + // 6.Suspend methodContext. + + // 7.Set generator.[[GeneratorState]] to executing. + generator->SetGeneratorState(thread, JSTaggedValue(static_cast(JSGeneratorState::EXECUTING))); + + // 8.Push genContext onto the execution context stack; genContext is now the running execution context. + C2IBridge c2i; + GeneratorHelper::ChangeGenContext(thread, genContext, &c2i); + + // 9.Resume the suspended evaluation of genContext using NormalCompletion(value) as the result of the operation + // that suspended it. Let result be the value returned by the resumed computation. + // 10.Assert: When we return here, genContext has already been removed from the execution context stack and + // methodContext is the currently running execution context. + // 11.Return Completion(result). + JSHandle result = GeneratorHelper::Next(thread, genContext, value); + GeneratorHelper::ResumeContext(thread); + return result; +} + +JSHandle JSGeneratorObject::GeneratorResumeAbrupt(JSThread *thread, + const JSHandle &generator, + const JSHandle &abruptCompletion) +{ + // 1.Let state be ? GeneratorValidate(generator). + JSHandle gen(thread, generator.GetTaggedValue()); + JSTaggedValue state = GeneratorValidate(thread, gen); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + + // 2.If state is suspendedStart, then + // a.Set generator.[[GeneratorState]] to completed. + // b.Once a generator enters the completed state it never leaves it and its associated execution context is + // never resumed. Any execution state associated with generator can be discarded at this point. + // c.Set state to completed. + if (state == JSTaggedValue(static_cast(JSGeneratorState::SUSPENDED_START))) { + generator->SetGeneratorState(thread, JSTaggedValue(static_cast(JSGeneratorState::COMPLETED))); + state = JSTaggedValue(static_cast(JSGeneratorState::COMPLETED)); + } + + // 3.If state is completed, then + // a.If abruptCompletion.[[Type]] is return, then + // i.Return CreateIterResultObject(abruptCompletion.[[Value]], true). + // b.Return Completion(abruptCompletion). + if (state == JSTaggedValue(static_cast(JSGeneratorState::COMPLETED))) { + JSHandle valueHandle(thread, abruptCompletion->GetValue()); + JSHandle result = JSIterator::CreateIterResultObject(thread, valueHandle, true); + if (abruptCompletion->GetType() == JSTaggedValue(static_cast(CompletionRecord::RETURN))) { + return result; + } + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, valueHandle.GetTaggedValue(), result); + } + + // 4.Assert: state is suspendedYield. + ASSERT_PRINT(state == JSTaggedValue(static_cast(JSGeneratorState::SUSPENDED_YIELD)), + "state is not suspendedYield"); + + // 5.Let genContext be generator.[[GeneratorContext]]. + JSHandle genContext(thread, generator->GetGeneratorContext()); + + // 6.Let methodContext be the running execution context. + // 7.Suspend methodContext. + + // 8.Set generator.[[GeneratorState]] to executing. + generator->SetGeneratorState(thread, JSTaggedValue(static_cast(JSGeneratorState::EXECUTING))); + + // 9.Push genContext onto the execution context stack; genContext is now the running execution context. + C2IBridge c2i; + GeneratorHelper::ChangeGenContext(thread, genContext, &c2i); + + // 10.Resume the suspended evaluation of genContext using abruptCompletion as the result of the operation that + // suspended it. Let result be the completion record returned by the resumed computation. + // 11.Assert: When we return here, genContext has already been removed from the execution context stack and + // methodContext is the currently running execution context. + // 12.Return Completion(result). + JSHandle result; + if (abruptCompletion->GetType() == JSTaggedValue(static_cast(CompletionRecord::RETURN))) { + result = GeneratorHelper::Return(thread, genContext, abruptCompletion->GetValue()); + } else { + result = GeneratorHelper::Throw(thread, genContext, abruptCompletion->GetValue()); + } + GeneratorHelper::ResumeContext(thread); + return result; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_generator_object.h b/ecmascript/js_generator_object.h new file mode 100644 index 0000000000000000000000000000000000000000..97b4a943f49f97fc632e74e7a33bbbafa5392d29 --- /dev/null +++ b/ecmascript/js_generator_object.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_GENERATOR_OBJECT_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_GENERATOR_OBJECT_H + +#include "ecmascript/js_function.h" + +namespace panda { +namespace ecmascript { +enum class JSGeneratorState { + UNDEFINED = 0, + SUSPENDED_START, + SUSPENDED_YIELD, + EXECUTING, + COMPLETED, +}; + +class GeneratorContext : TaggedObject { +public: + static GeneratorContext *Cast(ObjectHeader *object) + { + ASSERT(!JSTaggedValue(object).IsJSHClass()); + return static_cast(object); + } + + static constexpr size_t GENERATOR_REGS_ARRAY_OFFSET = sizeof(TaggedObject); + ACCESSORS(RegsArray, GENERATOR_REGS_ARRAY_OFFSET, GENERATOR_METHOD_OFFSET) + ACCESSORS(Method, GENERATOR_METHOD_OFFSET, GENERATOR_ACC_OFFSET) + ACCESSORS(Acc, GENERATOR_ACC_OFFSET, GENERATOR_NREGS_OFFSET) + ACCESSORS(NRegs, GENERATOR_NREGS_OFFSET, GENERATOR_BC_OFFSET_OFFSET) + ACCESSORS(BCOffset, GENERATOR_BC_OFFSET_OFFSET, GENERATOR_GENERATOR_OBJECT_OFFSET) + ACCESSORS(GeneratorObject, GENERATOR_GENERATOR_OBJECT_OFFSET, GENERATOR_LEXICALENV_OFFSET) + ACCESSORS(LexicalEnv, GENERATOR_LEXICALENV_OFFSET, SIZE) + + DECL_VISIT_OBJECT(GENERATOR_REGS_ARRAY_OFFSET, SIZE) +}; + +class JSGeneratorObject : public JSObject { +public: + static JSGeneratorObject *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsGeneratorObject()); + return static_cast(object); + } + + static constexpr size_t GENERATOR_STATE_OFFSET = JSObject::SIZE; + ACCESSORS(GeneratorState, GENERATOR_STATE_OFFSET, GENERATOR_CONTEXT_OFFSET) + ACCESSORS(GeneratorContext, GENERATOR_CONTEXT_OFFSET, GENERATOR_RESUME_RESULT_OFFSET) + ACCESSORS(ResumeResult, GENERATOR_RESUME_RESULT_OFFSET, GENERATOR_RESUME_MODE_OFFSET) + ACCESSORS(ResumeMode, GENERATOR_RESUME_MODE_OFFSET, SIZE) + + // 26.4.3.2 GeneratorValidate(generator) + static JSTaggedValue GeneratorValidate(JSThread *thread, const JSHandle &obj); + + // 26.4.3.3 GeneratorResume(generator, value) + static JSHandle GeneratorResume(JSThread *thread, const JSHandle &generator, + JSTaggedValue value); + + // 26.4.3.4 GeneratorResumeAbrupt(generator, abruptCompletion) + static JSHandle GeneratorResumeAbrupt(JSThread *thread, const JSHandle &generator, + const JSHandle &abruptCompletion); + + inline bool IsSuspendYield() const + { + return GetGeneratorState() == JSTaggedValue(static_cast(JSGeneratorState::SUSPENDED_YIELD)); + } + + inline bool IsExecuting() const + { + return GetGeneratorState() == JSTaggedValue(static_cast(JSGeneratorState::EXECUTING)); + } + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, GENERATOR_STATE_OFFSET, SIZE) +}; + +class JSAsyncFuncObject : public JSGeneratorObject { +public: + static JSAsyncFuncObject *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsAsyncFuncObject()); + return static_cast(object); + } + + static constexpr size_t GENERATOR_PROMISE_OFFSET = JSGeneratorObject::SIZE; + ACCESSORS(Promise, GENERATOR_PROMISE_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSGeneratorObject, GENERATOR_PROMISE_OFFSET, SIZE) +}; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_GENERATOR_OBJECT_H diff --git a/ecmascript/js_global_object.h b/ecmascript/js_global_object.h new file mode 100644 index 0000000000000000000000000000000000000000..3f60b5e18de06844aed8a03c26bf43228a4dc46a --- /dev/null +++ b/ecmascript/js_global_object.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSGLOBALOBJECT_H +#define PANDA_RUNTIME_ECMASCRIPT_JSGLOBALOBJECT_H + +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class JSGlobalObject : public JSObject { +public: + static JSGlobalObject *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsECMAObject()); + return static_cast(object); + } + + static constexpr size_t SIZE = JSObject::SIZE; + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, SIZE, SIZE) +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSGLOBALOBJECT_H diff --git a/ecmascript/js_handle.h b/ecmascript/js_handle.h new file mode 100644 index 0000000000000000000000000000000000000000..925817dbc126518eb2e0cb623aa8952959c41162 --- /dev/null +++ b/ecmascript/js_handle.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSHANDLE_H +#define PANDA_RUNTIME_ECMASCRIPT_JSHANDLE_H + +#include + +#include "ecmascript/ecma_handle_scope-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "handle_base.h" + +namespace panda::test { +class JSHandleTest; +} // namespace panda::test + +namespace panda::ecmascript { +class TaggedArray; +class LinkedHashMap; +class LinkedHashSet; +class NameDictionary; + +template +class JSHandle : public HandleBase { +public: + inline explicit JSHandle() : HandleBase(reinterpret_cast(nullptr)) {} + ~JSHandle() = default; + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(JSHandle); + DEFAULT_COPY_SEMANTIC(JSHandle); + + explicit JSHandle(const JSThread *thread, JSTaggedValue value) : HandleBase() + { + address_ = EcmaHandleScope::NewHandle(const_cast(thread), value.GetRawData()); + } + + explicit JSHandle(const JSThread *thread, const ObjectHeader *value) : HandleBase() + { + address_ = EcmaHandleScope::NewHandle(const_cast(thread), JSTaggedValue(value).GetRawData()); + } + + explicit JSHandle(const JSThread *thread, const TaggedObject *value) : HandleBase() + { + address_ = EcmaHandleScope::NewHandle(const_cast(thread), JSTaggedValue(value).GetRawData()); + } + + template + explicit JSHandle(const JSHandle &handle) : HandleBase(handle.GetAddress()) + { + } + + template + inline static JSHandle Cast(const JSHandle &handle) + { + T::Cast(handle.GetTaggedValue().GetTaggedObject()); + return JSHandle(handle.GetAddress()); + } + + inline JSTaggedValue GetTaggedValue() const + { + ASSERT(GetAddress() != 0U); + return *(reinterpret_cast(GetAddress())); // NOLINT(clang-analyzer-core.NullDereference) + } + + inline JSTaggedType GetTaggedType() const + { + ASSERT(GetAddress() != 0U); + return *reinterpret_cast(GetAddress()); // NOLINT(clang-analyzer-core.NullDereference) + } + + inline T *operator*() const + { + return T::Cast(GetTaggedValue().GetTaggedObject()); + } + + inline T *operator->() const + { + return T::Cast(GetTaggedValue().GetTaggedObject()); + } + + inline bool operator==(const JSHandle &other) const + { + return GetTaggedType() == other.GetTaggedType(); + } + + inline bool operator!=(const JSHandle &other) const + { + return GetTaggedType() != other.GetTaggedType(); + } + + inline bool IsEmpty() const + { + return GetAddress() == 0U; + } + + template + R *GetObject() const + { + return reinterpret_cast(GetTaggedValue().GetTaggedObject()); + } + + inline explicit JSHandle(uintptr_t slot) : HandleBase(slot) + { + if (!std::is_convertible::value) { + T::Cast((*reinterpret_cast(slot)).GetTaggedObject()); + } + } + + void Dump(JSThread *thread) const DUMP_API_ATTR + { + GetTaggedValue().Dump(thread); + } + +private: + inline explicit JSHandle(const JSTaggedType *slot) : HandleBase(reinterpret_cast(slot)) {} + inline explicit JSHandle(const T * const *slot) : HandleBase(reinterpret_cast(slot)) {} + + friend class EcmaVM; + friend class GlobalEnv; + friend class JSHandleTest; + friend class GlobalHandleCollection; +}; + +template <> +inline JSTaggedValue *JSHandle::operator->() const +{ + return reinterpret_cast(GetAddress()); +} + +template <> +inline JSTaggedValue *JSHandle::operator*() const +{ + return reinterpret_cast(GetAddress()); +} + +template <> +inline JSTaggedNumber *JSHandle::operator->() const +{ + return reinterpret_cast(GetAddress()); +} + +template <> +inline JSTaggedNumber *JSHandle::operator*() const +{ + return reinterpret_cast(GetAddress()); +} + +template +class JSMutableHandle : public JSHandle { +public: + JSMutableHandle() = default; + ~JSMutableHandle() = default; + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(JSMutableHandle); + DEFAULT_COPY_SEMANTIC(JSMutableHandle); + + explicit JSMutableHandle(const JSThread *thread, JSTaggedValue value) : JSHandle(thread, value) {} + explicit JSMutableHandle(const JSThread *thread, const TaggedArray *value) : JSHandle(thread, value) {} + + template + explicit JSMutableHandle(const JSHandle &handle) : JSHandle(handle) + { + } + + void Update(JSTaggedValue value) + { + auto addr = reinterpret_cast(this->GetAddress()); + *addr = value; + } +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSHANDLE_H diff --git a/ecmascript/js_hclass-inl.h b/ecmascript/js_hclass-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..a199cb54c733e38d425326b6ba1f84a960ec9510 --- /dev/null +++ b/ecmascript/js_hclass-inl.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_HCLASS_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_HCLASS_INL_H + +#include "ecmascript/js_hclass.h" + +#include "ecmascript/layout_info-inl.h" +#include "ecmascript/transitions_dictionary.h" +#include "ecmascript/mem/assert_scope.h" + +namespace panda::ecmascript { +inline JSHClass *JSHClass::Cast(const TaggedObject *object) +{ + ASSERT(JSTaggedValue(object).IsJSHClass()); + return static_cast(const_cast(object)); +} + +void JSHClass::AddTransitions(const JSThread *thread, const JSHandle &parent, const JSHandle &child, + const JSHandle &key, PropertyAttributes attributes) +{ + JSTaggedValue transitions = parent->GetTransitions(); + if (transitions.IsNull()) { + parent->SetTransitions(thread, child.GetTaggedValue()); + child->SetParent(thread, parent.GetTaggedValue()); + return; + } + if (transitions.IsJSHClass()) { + auto cachedHClass = JSHClass::Cast(transitions.GetTaggedObject()); + int last = cachedHClass->GetPropertiesNumber() - 1; + LayoutInfo *layoutInfo = LayoutInfo::Cast(cachedHClass->GetAttributes().GetTaggedObject()); + auto attr = JSHandle(thread, JSTaggedValue(layoutInfo->GetAttr(last).GetPropertyMetaData())); + auto lastKey = JSHandle(thread, layoutInfo->GetKey(last)); + auto lastHClass = JSHandle(thread, cachedHClass); + auto dict = JSHandle(thread, TransitionsDictionary::Create(thread)); + transitions = JSTaggedValue(TransitionsDictionary::PutIfAbsent(thread, dict, lastKey, lastHClass, attr)); + } + auto attr = JSHandle(thread, JSTaggedValue(attributes.GetPropertyMetaData())); + JSHandle dict(thread, transitions); + transitions = + JSTaggedValue(TransitionsDictionary::PutIfAbsent(thread, dict, key, JSHandle(child), attr)); + parent->SetTransitions(thread, transitions); + child->SetParent(thread, parent.GetTaggedValue()); +} + +void JSHClass::AddExtensionTransitions(const JSThread *thread, const JSHandle &parent, + const JSHandle &child, const JSHandle &key) +{ + auto attr = JSHandle(thread, PropertyAttributes(0).GetTaggedValue()); + AddProtoTransitions(thread, parent, child, key, attr); +} + +void JSHClass::AddProtoTransitions(const JSThread *thread, const JSHandle &parent, + const JSHandle &child, const JSHandle &key, + const JSHandle &proto) +{ + JSTaggedValue transitions = parent->GetTransitions(); + if (transitions.IsNull()) { + transitions = JSTaggedValue(TransitionsDictionary::Create(thread)); + } else if (transitions.IsJSHClass()) { + auto cachedHClass = JSHClass::Cast(transitions.GetTaggedObject()); + int last = cachedHClass->GetPropertiesNumber() - 1; + LayoutInfo *layoutInfo = LayoutInfo::Cast(cachedHClass->GetAttributes().GetTaggedObject()); + auto attr = JSHandle(thread, JSTaggedValue(layoutInfo->GetAttr(last).GetPropertyMetaData())); + auto lastKey = JSHandle(thread, layoutInfo->GetKey(last)); + auto lastHClass = JSHandle(thread, cachedHClass); + auto dict = JSHandle(thread, TransitionsDictionary::Create(thread)); + transitions = JSTaggedValue(TransitionsDictionary::PutIfAbsent(thread, dict, lastKey, lastHClass, attr)); + } + + JSHandle dict(thread, transitions); + transitions = + JSTaggedValue(TransitionsDictionary::PutIfAbsent(thread, dict, key, JSHandle(child), proto)); + parent->SetTransitions(thread, transitions); + child->SetParent(thread, parent.GetTaggedValue()); +} + +inline JSHClass *JSHClass::FindTransitions(const JSTaggedValue &key, const JSTaggedValue &attributes) +{ + DISALLOW_GARBAGE_COLLECTION; + JSTaggedValue transitions = GetTransitions(); + if (transitions.IsNull()) { + return nullptr; + } + if (transitions.IsJSHClass()) { + auto cachedHClass = JSHClass::Cast(transitions.GetTaggedObject()); + int last = cachedHClass->GetPropertiesNumber() - 1; + LayoutInfo *layoutInfo = LayoutInfo::Cast(cachedHClass->GetAttributes().GetTaggedObject()); + auto attr = layoutInfo->GetAttr(last).GetPropertyMetaData(); + auto cachedKey = layoutInfo->GetKey(last); + if (attr == attributes.GetInt() && key == cachedKey) { + return cachedHClass; + } + return nullptr; + } + + ASSERT(transitions.IsTaggedArray()); + TransitionsDictionary *dict = TransitionsDictionary::Cast(transitions.GetTaggedObject()); + auto entry = dict->FindEntry(key, attributes); + if (entry == -1) { + return nullptr; + } + return JSHClass::Cast(dict->GetValue(entry).GetTaggedObject()); +} + +inline JSHClass *JSHClass::FindProtoTransitions(const JSTaggedValue &key, const JSTaggedValue &proto) +{ + DISALLOW_GARBAGE_COLLECTION; + JSTaggedValue transitions = GetTransitions(); + if (!transitions.IsTaggedArray()) { + ASSERT(transitions.IsNull() || transitions.IsJSHClass()); + return nullptr; + } + ASSERT(transitions.IsTaggedArray()); + TransitionsDictionary *dict = TransitionsDictionary::Cast(transitions.GetTaggedObject()); + auto entry = dict->FindEntry(key, proto); + if (entry == -1) { + return nullptr; + } + return JSHClass::Cast(dict->GetValue(entry).GetTaggedObject()); +} + +inline void JSHClass::UpdatePropertyMetaData(const JSThread *thread, [[maybe_unused]] const JSTaggedValue &key, + const PropertyAttributes &metaData) +{ + DISALLOW_GARBAGE_COLLECTION; + ASSERT(!GetAttributes().IsNull()); + LayoutInfo *layoutInfo = LayoutInfo::Cast(GetAttributes().GetTaggedObject()); + ASSERT(layoutInfo->GetLength() != 0); + int entry = metaData.GetOffset(); + + layoutInfo->SetNormalAttr(thread, entry, metaData); +} + +inline bool JSHClass::HasReferenceField(JSType type) +{ + return type != JSType::STRING && type != JSType::JS_NATIVE_POINTER; +} + +inline size_t JSHClass::SizeFromJSHClass(JSType type, TaggedObject *header) +{ + if (type == JSType::TAGGED_ARRAY || type == JSType::TAGGED_DICTIONARY) { + auto length = reinterpret_cast(header)->GetLength(); + return TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + } + + if (type == JSType::STRING) { + return reinterpret_cast(header)->ObjectSize(); + } + ASSERT(GetObjectSize() != 0); + return GetObjectSize(); +} + +inline void JSHClass::Copy(const JSThread *thread, const JSHClass *jshcalss) +{ + DISALLOW_GARBAGE_COLLECTION; + + // copy jshclass + SetPrototype(thread, jshcalss->GetPrototype()); + SetBitField(jshcalss->GetBitField()); +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_HCLASS_INL_H diff --git a/ecmascript/js_hclass.cpp b/ecmascript/js_hclass.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a0b6dbe33e3603029b795c48e98e8736ffcbe785 --- /dev/null +++ b/ecmascript/js_hclass.cpp @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_hclass-inl.h" + +#include + +#include "ecmascript/global_env.h" +#include "ecmascript/ic/proto_change_details.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_symbol.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/weak_vector-inl.h" + +namespace panda::ecmascript { +#define ENABLE_IC 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) + +// class TransitionsDictionary +TransitionsDictionary *TransitionsDictionary::PutIfAbsent(const JSThread *thread, + const JSHandle &dictionary, + const JSHandle &key, + const JSHandle &value, + const JSHandle &metaData) +{ + int hash = TransitionsDictionary::Hash(key.GetTaggedValue(), metaData.GetTaggedValue()); + + /* no need to add key if exist */ + int entry = dictionary->FindEntry(key.GetTaggedValue(), metaData.GetTaggedValue()); + if (entry != -1) { + return *dictionary; + } + + // Check whether the dictionary should be extended. + TransitionsDictionary *newDictionary = HashTableT::GrowHashTable(thread, dictionary); + // Compute the key object. + entry = newDictionary->FindInsertIndex(hash); + newDictionary->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), metaData.GetTaggedValue()); + + newDictionary->IncreaseEntries(thread); + return newDictionary; +} + +int TransitionsDictionary::FindEntry(const JSTaggedValue &key, const JSTaggedValue &metaData) +{ + size_t size = Size(); + int count = 1; + int hash = TransitionsDictionary::Hash(key, metaData); + // GrowHashTable will guarantee the hash table is never full. + for (int entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) { + JSTaggedValue element = GetKey(entry); + if (element.IsHole()) { + continue; + } + if (element.IsUndefined()) { + return -1; + } + + if (TransitionsDictionary::IsMatch(key, metaData, element, GetAttributes(entry))) { + return entry; + } + } + return -1; +} + +TransitionsDictionary *TransitionsDictionary::Remove(const JSThread *thread, + const JSHandle &table, + const JSHandle &key, const JSTaggedValue &metaData) +{ + int entry = table->FindEntry(key.GetTaggedValue(), metaData); + if (entry == -1) { + return *table; + } + + table->RemoveElement(thread, entry); + return TransitionsDictionary::Shrink(thread, table); +} + +void TransitionsDictionary::Rehash(const JSThread *thread, TransitionsDictionary *newTable) +{ + DISALLOW_GARBAGE_COLLECTION; + if ((newTable == nullptr) || (newTable->Size() < EntriesCount())) { + return; + } + int size = this->Size(); + // Rehash elements to new table + for (int i = 0; i < size; i++) { + int fromIndex = GetEntryIndex(i); + JSTaggedValue k = this->GetKey(i); + if (!IsKey(k)) { + continue; + } + int hash = TransitionsDictionary::Hash(k, this->GetAttributes(i)); + int insertionIndex = GetEntryIndex(newTable->FindInsertIndex(hash)); + JSTaggedValue tv = Get(fromIndex); + newTable->Set(thread, insertionIndex, tv); + for (int j = 1; j < TransitionsDictionary::ENTRY_SIZE; j++) { + tv = Get(fromIndex + j); + newTable->Set(thread, insertionIndex + j, tv); + } + } + newTable->SetEntriesCount(thread, EntriesCount()); + newTable->SetHoleEntriesCount(thread, 0); +} + +// class JSHClass +void JSHClass::Initialize(const JSThread *thread, uint32_t size, JSType type, JSTaggedValue proto) +{ + DISALLOW_GARBAGE_COLLECTION; + if (JSType::JS_OBJECT_BEGIN <= type && type <= JSType::JS_OBJECT_END) { + SetObjectSize(size + DEFAULT_CAPACITY_OF_IN_OBJECTS * JSTaggedValue::TaggedTypeSize()); + auto env = thread->GetEcmaVM()->GetGlobalEnv(); + SetAttributes(thread, env->GetEmptyLayoutInfo()); + } else { + SetObjectSize(size); + SetAttributes(thread, JSTaggedValue::Null()); + } + SetPrototype(thread, proto); + + ClearBitField(); + SetObjectType(type); + SetExtensible(true); + SetIsPrototype(false); + SetElementRepresentation(Representation::NONE); + SetUnusedInlinedProps(DEFAULT_CAPACITY_OF_IN_OBJECTS); + SetUnusedNonInlinedProps(DEFAULT_CAPACITY_OF_OUT_OBJECTS); + SetTransitions(thread, JSTaggedValue::Null()); + SetParent(thread, JSTaggedValue::Null()); + SetProtoChangeMarker(thread, JSTaggedValue::Null()); + SetProtoChangeDetails(thread, JSTaggedValue::Null()); + SetEnumCache(thread, JSTaggedValue::Null()); +} + +JSHandle JSHClass::Clone(const JSThread *thread, const JSHandle &jshclass) +{ + JSType type = jshclass->GetObjectType(); + uint32_t size = jshclass->GetObjectSize() - DEFAULT_CAPACITY_OF_IN_OBJECTS * JSTaggedValue::TaggedTypeSize(); + + JSHandle newJshclass = thread->GetEcmaVM()->GetFactory()->NewEcmaDynClass(size, type); + // 3. Copy + JSHClass::CopyAll(thread, newJshclass, jshclass); + return newJshclass; +} + +void JSHClass::CopyAll(const JSThread *thread, const JSHandle &newJshclass, + const JSHandle &jshclass) +{ + newJshclass->Copy(thread, *jshclass); + + newJshclass->SetParent(thread, JSTaggedValue::Null()); + newJshclass->SetTransitions(thread, JSTaggedValue::Null()); + newJshclass->SetProtoChangeDetails(thread, JSTaggedValue::Null()); + newJshclass->SetEnumCache(thread, JSTaggedValue::Null()); + // reuse Attributes first. + newJshclass->SetAttributes(thread, jshclass->GetAttributes()); +} + +void JSHClass::TransitionElementsToDictionary(const JSThread *thread, const JSHandle &obj) +{ + // property transition to slow first + if (!obj->GetJSHClass()->IsDictionaryMode()) { + JSObject::TransitionToDictionary(thread, obj); + } + obj->GetJSHClass()->SetIsDictionaryElement(true); + obj->GetJSHClass()->SetIsStableJSArray(false); +} + +void JSHClass::AddProperty(const JSThread *thread, const JSHandle &obj, const JSHandle &key, + const PropertyAttributes &attr) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle jshclass(thread, obj->GetJSHClass()); + JSHClass *newDyn = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(attr.GetPropertyMetaData())); + if (newDyn != nullptr) { +#if ENABLE_IC + JSHClass::NotifyHclassChanged(thread, jshclass, JSHandle(thread, newDyn)); +#endif + obj->SetClass(newDyn); + return; + } + + // 2. Create hclass + JSType type = jshclass->GetObjectType(); + uint32_t size = jshclass->GetObjectSize() - DEFAULT_CAPACITY_OF_IN_OBJECTS * JSTaggedValue::TaggedTypeSize(); + JSHandle newJshclass = factory->NewEcmaDynClass(size, type); + + // 3. Copy field and properties + JSHClass::CopyAll(thread, newJshclass, jshclass); + + // 4. Add Property and metaData + bool inObj = attr.IsInlinedProps(); + int offset = attr.GetOffset(); + if (inObj) { + newJshclass->DecUnusedInlinedProps(); + } else { + newJshclass->DecUnusedNonInlinedProps(); + } + + { + JSMutableHandle layoutInfoHandle(thread, newJshclass->GetAttributes()); + + if (layoutInfoHandle->NumberOfElements() != offset) { + layoutInfoHandle.Update(factory->CopyAndReSort(layoutInfoHandle, offset, offset + 1).GetTaggedValue()); + newJshclass->SetAttributes(thread, layoutInfoHandle); + } else if (layoutInfoHandle->GetPropertiesCapacity() <= offset) { // need to Grow + layoutInfoHandle.Update(factory->ExtendLayoutInfo(layoutInfoHandle, + LayoutInfo::ComputeGrowCapacity(offset)).GetTaggedValue()); + newJshclass->SetAttributes(thread, layoutInfoHandle); + } + layoutInfoHandle->AddKey(thread, offset, key.GetTaggedValue(), attr); + } + + // 5. Add newDynclass to old dynclass's transitions. + AddTransitions(thread, jshclass, newJshclass, key, attr); + + // 6. update hclass in object. +#if ENABLE_IC + JSHClass::NotifyHclassChanged(thread, jshclass, newJshclass); +#endif + obj->SetClass(*newJshclass); +} + +JSHandle JSHClass::TransitionExtension(const JSThread *thread, const JSHandle &jshclass) +{ + JSHandle key(thread->GlobalConstants()->GetHandledPreventExtensionsString()); + { + auto *newDyn = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(0)); + if (newDyn != nullptr) { + return JSHandle(thread, newDyn); + } + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 2. new a hclass + JSType type = jshclass->GetObjectType(); + uint32_t size = jshclass->GetObjectSize() - DEFAULT_CAPACITY_OF_IN_OBJECTS * JSTaggedValue::TaggedTypeSize(); + JSHandle newJshclass = factory->NewEcmaDynClass(size, type); + + // 3. Copy and change + JSHClass::CopyAll(thread, newJshclass, jshclass); + newJshclass->SetExtensible(false); + + JSTaggedValue attrs = newJshclass->GetAttributes(); + { + JSMutableHandle layoutInfoHandle(thread, attrs); + layoutInfoHandle.Update(factory->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue()); + newJshclass->SetAttributes(thread, layoutInfoHandle); + } + + // 4. Add newDynclass to old dynclass's parent's transitions. + AddExtensionTransitions(thread, jshclass, newJshclass, key); + // parent is the same as jshclass, already copy + return newJshclass; +} + +JSHandle JSHClass::TransitionProto(const JSThread *thread, const JSHandle &jshclass, + const JSHandle &proto) +{ + JSHandle key(thread->GlobalConstants()->GetHandledPrototypeString()); + + { + auto *newDyn = jshclass->FindProtoTransitions(key.GetTaggedValue(), proto.GetTaggedValue()); + if (newDyn != nullptr) { + return JSHandle(thread, newDyn); + } + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 2. new a hclass + JSType type = jshclass->GetObjectType(); + uint32_t size = jshclass->GetObjectSize() - DEFAULT_CAPACITY_OF_IN_OBJECTS * JSTaggedValue::TaggedTypeSize(); + JSHandle newJshclass = factory->NewEcmaDynClass(size, type); + + // 3. Copy + JSHClass::CopyAll(thread, newJshclass, jshclass); + newJshclass->SetPrototype(thread, proto.GetTaggedValue()); + + JSTaggedValue attrs = newJshclass->GetAttributes(); + { + JSMutableHandle layoutInfoHandle(thread, attrs); + layoutInfoHandle.Update(factory->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue()); + newJshclass->SetAttributes(thread, layoutInfoHandle); + } + + // 4. Add newJshclass to old jshclass's parent's transitions. + AddProtoTransitions(thread, jshclass, newJshclass, key, proto); + + // parent is the same as jshclass, already copy + return newJshclass; +} + +void JSHClass::SetPrototype(const JSThread *thread, JSTaggedValue proto) +{ + if (proto.IsECMAObject()) { + JSObject::Cast(proto.GetTaggedObject())->GetJSHClass()->SetIsPrototype(true); + } + SetProto(thread, proto); +} +void JSHClass::SetPrototype(const JSThread *thread, const JSHandle &proto) +{ + if (proto->IsECMAObject()) { + JSObject::Cast(proto->GetTaggedObject())->GetJSHClass()->SetIsPrototype(true); + } + SetProto(thread, proto); +} + +void JSHClass::TransitionToDictionary(const JSThread *thread, const JSHandle &obj) +{ + // 1. new a hclass + JSType type = obj->GetJSHClass()->GetObjectType(); + uint32_t size = + obj->GetJSHClass()->GetObjectSize() - DEFAULT_CAPACITY_OF_IN_OBJECTS * JSTaggedValue::TaggedTypeSize(); + JSHandle newJshclass = thread->GetEcmaVM()->GetFactory()->NewEcmaDynClass(size, type); + { + DISALLOW_GARBAGE_COLLECTION; + // 2. Copy + newJshclass->Copy(thread, obj->GetJSHClass()); + + // reset Unused + newJshclass->SetUnusedInlinedProps(0); + newJshclass->SetUnusedNonInlinedProps(0); + newJshclass->SetIsDictionaryMode(true); + + // 3. Add newJshclass to ? +#if ENABLE_IC + JSHClass::NotifyHclassChanged(thread, JSHandle(thread, obj->GetJSHClass()), newJshclass); +#endif + obj->SetClass(newJshclass); + } +} + +JSHandle JSHClass::EnableProtoChangeMarker(const JSThread *thread, const JSHandle &jshclass) +{ + JSTaggedValue proto = jshclass->GetPrototype(); + if (!proto.IsECMAObject()) { + // Return JSTaggedValue directly. No proto check is needed. + return JSHandle(thread, JSTaggedValue(false)); + } + JSHandle protoHandle(thread, proto); + JSHandle protoDyncalss(thread, protoHandle->GetJSHClass()); + RegisterOnProtoChain(thread, protoDyncalss); + JSTaggedValue protoChangeMarker = protoDyncalss->GetProtoChangeMarker(); + if (protoChangeMarker.IsProtoChangeMarker()) { + JSHandle markerHandle(thread, ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject())); + if (!markerHandle->GetHasChanged()) { + return JSHandle(markerHandle); + } + } + JSHandle markerHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeMarker(); + markerHandle->SetHasChanged(false); + protoDyncalss->SetProtoChangeMarker(thread, markerHandle.GetTaggedValue()); + return JSHandle(markerHandle); +} + +void JSHClass::NotifyHclassChanged(const JSThread *thread, JSHandle oldHclass, JSHandle newHclass) +{ + if (!oldHclass->IsPrototype()) { + return; + } + // The old hclass is the same as new one + if (oldHclass.GetTaggedValue() == newHclass.GetTaggedValue()) { + return; + } + newHclass->SetIsPrototype(true); + JSHClass::NoticeThroughChain(thread, oldHclass); + JSHClass::RefreshUsers(thread, oldHclass, newHclass); +} + +void JSHClass::RegisterOnProtoChain(const JSThread *thread, const JSHandle &jshclass) +{ + ASSERT(jshclass->IsPrototype()); + JSHandle user = jshclass; + JSHandle userDetails = GetProtoChangeDetails(thread, user); + + while (true) { + // Find the prototype chain as far as the hclass has not been registered. + if (userDetails->GetRegisterIndex() != JSTaggedValue(ProtoChangeDetails::UNREGISTERED)) { + return; + } + + JSTaggedValue proto = user->GetPrototype(); + if (!proto.IsHeapObject()) { + return; + } + if (proto.IsJSProxy()) { + return; + } + ASSERT(proto.IsECMAObject()); + JSHandle protoHandle(thread, proto); + JSHandle protoDetails = + GetProtoChangeDetails(thread, JSHandle(thread, protoHandle->GetJSHClass())); + JSTaggedValue listeners = protoDetails->GetChangeListener(); + JSHandle listenersHandle; + if (listeners == JSTaggedValue(0)) { + listenersHandle = JSHandle(ChangeListener::Create(thread)); + } else { + listenersHandle = JSHandle(thread, listeners); + } + array_size_t registerIndex = 0; + JSHandle newListeners = ChangeListener::Add(thread, listenersHandle, user, ®isterIndex); + userDetails->SetRegisterIndex(thread, JSTaggedValue(registerIndex)); + protoDetails->SetChangeListener(thread, newListeners.GetTaggedValue()); + userDetails = protoDetails; + user = JSHandle(thread, protoHandle->GetJSHClass()); + } +} + +bool JSHClass::UnregisterOnProtoChain(const JSThread *thread, const JSHandle &jshclass) +{ + ASSERT(jshclass->IsPrototype()); + if (!jshclass->GetProtoChangeDetails().IsProtoChangeDetails()) { + return false; + } + if (!jshclass->GetPrototype().IsECMAObject()) { + JSTaggedValue listeners = + ProtoChangeDetails::Cast(jshclass->GetProtoChangeDetails().GetTaggedObject())->GetChangeListener(); + return listeners != JSTaggedValue(0); + } + JSHandle currentDetails = GetProtoChangeDetails(thread, jshclass); + array_size_t index = currentDetails->GetRegisterIndex().GetArrayLength(); + if (JSTaggedValue(index) == JSTaggedValue(ProtoChangeDetails::UNREGISTERED)) { + return false; + } + JSTaggedValue proto = jshclass->GetPrototype(); + ASSERT(proto.IsECMAObject()); + JSTaggedValue protoDetailsValue = JSObject::Cast(proto.GetTaggedObject())->GetJSHClass()->GetProtoChangeDetails(); + ASSERT(protoDetailsValue.IsProtoChangeDetails()); + JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener(); + ASSERT(listenersValue != JSTaggedValue(0)); + JSHandle listeners(thread, listenersValue.GetTaggedObject()); + ASSERT(listeners->Get(index) == jshclass.GetTaggedValue()); + listeners->Delete(thread, index); + return true; +} + +JSHandle JSHClass::GetProtoChangeDetails(const JSThread *thread, const JSHandle &jshclass) +{ + JSTaggedValue protoDetails = jshclass->GetProtoChangeDetails(); + if (protoDetails.IsProtoChangeDetails()) { + return JSHandle(thread, protoDetails); + } + JSHandle protoDetailsHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeDetails(); + jshclass->SetProtoChangeDetails(thread, protoDetailsHandle.GetTaggedValue()); + return protoDetailsHandle; +} + +JSHandle JSHClass::GetProtoChangeDetails(const JSThread *thread, const JSHandle &obj) +{ + JSHandle jshclass(thread, obj->GetJSHClass()); + return GetProtoChangeDetails(thread, jshclass); +} + +void JSHClass::NoticeRegisteredUser([[maybe_unused]] const JSThread *thread, const JSHandle &jshclass) +{ + ASSERT(jshclass->IsPrototype()); + JSTaggedValue markerValue = jshclass->GetProtoChangeMarker(); + if (markerValue.IsProtoChangeMarker()) { + ProtoChangeMarker *protoChangeMarker = ProtoChangeMarker::Cast(markerValue.GetTaggedObject()); + protoChangeMarker->SetHasChanged(true); + } +} + +void JSHClass::NoticeThroughChain(const JSThread *thread, const JSHandle &jshclass) +{ + NoticeRegisteredUser(thread, jshclass); + JSTaggedValue protoDetailsValue = jshclass->GetProtoChangeDetails(); + if (!protoDetailsValue.IsProtoChangeDetails()) { + return; + } + JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener(); + if (!listenersValue.IsTaggedArray()) { + return; + } + ChangeListener *listeners = ChangeListener::Cast(listenersValue.GetTaggedObject()); + for (array_size_t i = 0; i < listeners->GetEnd(); i++) { + JSTaggedValue temp = listeners->Get(i); + if (temp.IsJSHClass()) { + NoticeThroughChain(thread, JSHandle(thread, listeners->Get(i).GetTaggedObject())); + } + } +} + +void JSHClass::RefreshUsers(const JSThread *thread, const JSHandle &oldHclass, + const JSHandle &newHclass) +{ + ASSERT(oldHclass->IsPrototype()); + ASSERT(newHclass->IsPrototype()); + bool onceRegistered = UnregisterOnProtoChain(thread, oldHclass); + + newHclass->SetProtoChangeDetails(thread, oldHclass->GetProtoChangeDetails()); + oldHclass->SetProtoChangeDetails(thread, JSTaggedValue(0)); + if (onceRegistered) { + if (newHclass->GetProtoChangeDetails().IsProtoChangeDetails()) { + ProtoChangeDetails::Cast(newHclass->GetProtoChangeDetails().GetTaggedObject()) + ->SetRegisterIndex(thread, JSTaggedValue(ProtoChangeDetails::UNREGISTERED)); + } + RegisterOnProtoChain(thread, newHclass); + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h new file mode 100644 index 0000000000000000000000000000000000000000..46e097218406abbb017aa436983239edbe2d7fdc --- /dev/null +++ b/ecmascript/js_hclass.h @@ -0,0 +1,900 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_HCLASS_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_HCLASS_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/mem/tagged_object.h" +#include "ecmascript/property_attributes.h" +#include "include/hclass.h" +#include "utils/bit_field.h" + +namespace panda::ecmascript { +class ProtoChangeDetails; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define JSTYPE_DECL /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + INVALID = 0, /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_OBJECT, /* JS_OBJECT_BEGIN ////////////////////////////////////////////////////////////////////// */ \ + JS_REALM, /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_FUNCTION_BASE, /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_FUNCTION, /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PROXY_REVOC_FUNCTION, /* /////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PROMISE_REACTIONS_FUNCTION, /* /////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PROMISE_EXECUTOR_FUNCTION, /* /////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION, /* //////////////////////////////////////////////////////-PADDING */ \ + JS_GENERATOR_FUNCTION, /* /////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ASYNC_FUNCTION, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_INTL_BOUND_FUNCTION, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ASYNC_AWAIT_STATUS_FUNCTION, /* ////////////////////////////////////////////////////////////////-PADDING */ \ + JS_BOUND_FUNCTION, /* //////////////////////////////////////////////////////////////////////////////////// */ \ + \ + JS_ERROR, /* JS_ERROR_BEGIN /////////////////////////////////////////////////////////////-PADDING */ \ + JS_EVAL_ERROR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_RANGE_ERROR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_REFERENCE_ERROR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_TYPE_ERROR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_URI_ERROR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_SYNTAX_ERROR, /* JS_ERROR_END /////////////////////////////////////////////////////////////////////// */ \ + \ + JS_REG_EXP, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_SET, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_MAP, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_WEAK_MAP, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_WEAK_SET, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_DATE, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ITERATOR, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_FORIN_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_MAP_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_SET_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ARRAY_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_STRING_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_INTL, /* ///////////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_LOCALE, /* /////////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_DATE_TIME_FORMAT, /* ///////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_RELATIVE_TIME_FORMAT, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_NUMBER_FORMAT, /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_COLLATOR, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PLURAL_RULES, /* ///////////////////////////////////////////////////////////////////////////////-PADDING */ \ + \ + JS_ARRAY_BUFFER, /* ///////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PROMISE, /* ///////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_DATA_VIEW, /* /////////////////////////////////////////////////////////////////////////////////////// */ \ + JS_ARGUMENTS, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_GENERATOR_OBJECT, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ASYNC_FUNC_OBJECT, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + \ + /* SPECIAL indexed objects begin, DON'T CHANGE HERE ///////////////////////////////////////////////-PADDING */ \ + JS_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_TYPED_ARRAY, /* JS_TYPED_ARRAY_BEGIN /////////////////////////////////////////////////////////////////// */ \ + JS_INT8_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_UINT8_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_UINT8_CLAMPED_ARRAY, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_INT16_ARRAY, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_UINT16_ARRAY, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_INT32_ARRAY, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_UINT32_ARRAY, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_FLOAT32_ARRAY, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_FLOAT64_ARRAY, /* JS_TYPED_ARRAY_END ///////////////////////////////////////////////////////////// */ \ + JS_PRIMITIVE_REF, /* number\boolean\string. SPECIAL indexed objects end, DON'T CHANGE HERE ////////-PADDING */ \ + JS_NATIVE_OBJECT, /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_GLOBAL_OBJECT, /* JS_OBJECT_END/////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PROXY, /* ECMA_OBJECT_END ////////////////////////////////////////////////////////////////////////////// */ \ + \ + HCLASS, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + STRING, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + TAGGED_ARRAY, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + TAGGED_DICTIONARY, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + FREE_OBJECT_WITH_ONE_FIELD, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ + FREE_OBJECT_WITH_NONE_FIELD, /* ///////////////////////////////////////////////////////////////////-PADDING */ \ + FREE_OBJECT_WITH_TWO_FIELD, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_NATIVE_POINTER, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + GLOBAL_ENV, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + ACCESSOR_DATA, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + INTERNAL_ACCESSOR, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + SYMBOL, /* ////////////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + OBJECT_WRAPPER, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_GENERATOR_CONTEXT, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + PROTOTYPE_HANDLER, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + TRANSITION_HANDLER, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + PROPERTY_BOX, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + PROTO_CHANGE_MARKER, /* ///////////////////////////////////////////////////////////////////////////-PADDING */ \ + PROTOTYPE_INFO, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + TEMPLATE_MAP, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + PROGRAM, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + LEXICAL_FUNCTION, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + \ + PROMISE_CAPABILITY, /* JS_RECORD_BEGIN //////////////////////////////////////////////////////////////////// */ \ + PROMISE_RECORD, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + RESOLVING_FUNCTIONS_RECORD, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ + PROMISE_REACTIONS, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ + PROMISE_ITERATOR_RECORD, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ + MICRO_JOB_QUEUE, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + PENDING_JOB, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + FUNCTION_EXTRA_INFO, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + COMPLETION_RECORD, /* JS_RECORD_END /////////////////////////////////////////////////////////////////////// */ \ + ECMA_MODULE, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_TYPE_LAST = ECMA_MODULE, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ + \ + JS_FUNCTION_BEGIN = JS_FUNCTION, /* ///////////////////////////////////////////////////////////////-PADDING */ \ + JS_FUNCTION_END = JS_ASYNC_AWAIT_STATUS_FUNCTION, /* //////////////////////////////////////////////-PADDING */ \ + \ + JS_OBJECT_BEGIN = JS_OBJECT, /* ///////////////////////////////////////////////////////////////////-PADDING */ \ + JS_OBJECT_END = JS_GLOBAL_OBJECT, /* //////////////////////////////////////////////////////////////-PADDING */ \ + \ + ECMA_OBJECT_BEGIN = JS_OBJECT, /* /////////////////////////////////////////////////////////////////-PADDING */ \ + ECMA_OBJECT_END = JS_PROXY, /* /////////////////////////////////////////////////////////////////-PADDING */ \ + \ + JS_ERROR_BEGIN = JS_ERROR, /* ////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ERROR_END = JS_SYNTAX_ERROR, /* ////////////////////////////////////////////////////////////////-PADDING */ \ + \ + JS_ITERATOR_BEGIN = JS_ITERATOR, /* //////////////////////////////////////////////////////////-PADDING */ \ + JS_ITERATOR_END = JS_STRING_ITERATOR, /* //////////////////////////////////////////////////////////-PADDING */ \ + \ + JS_RECORD_BEGIN = PROMISE_CAPABILITY, /* //////////////////////////////////////////////////////////-PADDING */ \ + JS_RECORD_END = COMPLETION_RECORD, /* ///////////////////////////////////////////////////////-PADDING */ \ + \ + JS_TYPED_ARRAY_BEGIN = JS_TYPED_ARRAY, /* /////////////////////////////////////////////////////////-PADDING */ \ + JS_TYPED_ARRAY_END = JS_FLOAT64_ARRAY /* /////////////////////////////////////////////////////////-PADDING */ + +enum class JSType : uint8_t { + JSTYPE_DECL, +}; + +class JSHClass : public TaggedObject { +public: + static constexpr int TYPE_BITFIELD_NUM = 8; + using ObjectTypeBits = BitField; // 7 + using CallableBit = ObjectTypeBits::NextFlag; + using ConstrutorBit = CallableBit::NextFlag; // 9 + using BuiltinsCtorBit = ConstrutorBit::NextFlag; // 10 + using ExtensibleBit = BuiltinsCtorBit::NextFlag; + using IsPrototypeBit = ExtensibleBit::NextFlag; + using ElementRepresentationBits = IsPrototypeBit::NextField; // 3 means next 3 bit + using DictionaryElementBits = ElementRepresentationBits::NextFlag; // 16 + using NumberOfUnusedInlinedPropsBits = DictionaryElementBits::NextField; // 3 means next 3 bit + // the max value is 1024, need 11 bits + using NumberOfUnusedNonInlinedPropsBits = + NumberOfUnusedInlinedPropsBits::NextField; // 29 + using IsLiteralBit = NumberOfUnusedNonInlinedPropsBits::NextFlag; + using ClassConstructorBit = IsLiteralBit::NextFlag; + using ClassPrototypeBit = ClassConstructorBit::NextFlag; + using IsDictionaryBit = ClassPrototypeBit::NextFlag; + using IsStableJSArrayBit = IsDictionaryBit::NextFlag; + using HasConstructorBits = IsStableJSArrayBit::NextFlag; + + static constexpr int DEFAULT_CAPACITY_OF_IN_OBJECTS = 4; + static constexpr int DEFAULT_CAPACITY_OF_OUT_OBJECTS = + PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES - DEFAULT_CAPACITY_OF_IN_OBJECTS; + + static JSHClass *Cast(const TaggedObject *object); + + inline size_t SizeFromJSHClass(JSType type, TaggedObject *header); + inline bool HasReferenceField(JSType type); + + // size need to add inlined property numbers + void Initialize(const JSThread *thread, uint32_t size, JSType type, JSTaggedValue proto); + + static JSHandle Clone(const JSThread *thread, const JSHandle &jshclass); + static void TransitionElementsToDictionary(const JSThread *thread, const JSHandle &obj); + static void AddProperty(const JSThread *thread, const JSHandle &obj, const JSHandle &key, + const PropertyAttributes &attr); + + static JSHandle TransitionExtension(const JSThread *thread, const JSHandle &jshclass); + static JSHandle TransitionProto(const JSThread *thread, const JSHandle &jshclass, + const JSHandle &proto); + static void TransitionToDictionary(const JSThread *thread, const JSHandle &obj); + + static JSHandle EnableProtoChangeMarker(const JSThread *thread, const JSHandle &jshclass); + + static void NotifyHclassChanged(const JSThread *thread, JSHandle oldHclass, JSHandle newHclass); + + static void RegisterOnProtoChain(const JSThread *thread, const JSHandle &jshclass); + + static bool UnregisterOnProtoChain(const JSThread *thread, const JSHandle &jshclass); + + static JSHandle GetProtoChangeDetails(const JSThread *thread, + const JSHandle &jshclass); + + static JSHandle GetProtoChangeDetails(const JSThread *thread, const JSHandle &obj); + + inline void UpdatePropertyMetaData(const JSThread *thread, const JSTaggedValue &key, + const PropertyAttributes &metaData); + + static void NoticeRegisteredUser(const JSThread *thread, const JSHandle &jshclass); + + static void NoticeThroughChain(const JSThread *thread, const JSHandle &jshclass); + + static void RefreshUsers(const JSThread *thread, const JSHandle &oldHclass, + const JSHandle &newHclass); + + inline void ClearBitField() + { + SetBitField(0ULL); + } + + inline JSType GetObjectType() const + { + uint64_t bits = GetBitField(); + return ObjectTypeBits::Decode(bits); + } + + inline void SetObjectType(JSType type) + { + uint64_t bits = GetBitField(); + uint64_t newVal = ObjectTypeBits::Update(bits, type); + SetBitField(newVal); + } + + inline void SetCallable(bool flag) + { + CallableBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetConstructor(bool flag) const + { + ConstrutorBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetBuiltinsCtor(bool flag) const + { + BuiltinsCtorBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetExtensible(bool flag) const + { + ExtensibleBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetIsPrototype(bool flag) const + { + IsPrototypeBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetIsLiteral(bool flag) const + { + IsLiteralBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetClassConstructor(bool flag) const + { + ClassConstructorBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetClassPrototype(bool flag) const + { + ClassPrototypeBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetIsDictionaryMode(bool flag) const + { + IsDictionaryBit::Set(flag, GetBitFieldAddr()); + } + + inline bool IsJSObject() const + { + JSType jsType = GetObjectType(); + return (JSType::JS_OBJECT_BEGIN <= jsType && jsType <= JSType::JS_OBJECT_END); + } + + inline bool IsECMAObject() const + { + JSType jsType = GetObjectType(); + return (JSType::ECMA_OBJECT_BEGIN <= jsType && jsType <= JSType::ECMA_OBJECT_END); + } + + inline bool IsRealm() const + { + return GetObjectType() == JSType::JS_REALM; + } + + inline bool IsHClass() const + { + return GetObjectType() == JSType::HCLASS; + } + + inline bool IsString() const + { + return GetObjectType() == JSType::STRING; + } + + inline bool IsStringOrSymbol() const + { + JSType jsType = GetObjectType(); + return (jsType == JSType::STRING) || (jsType == JSType::SYMBOL); + } + + inline bool IsTaggedArray() const + { + JSType jsType = GetObjectType(); + return jsType == JSType::TAGGED_ARRAY || jsType == JSType::TAGGED_DICTIONARY; + } + + inline bool IsDictionary() const + { + return GetObjectType() == JSType::TAGGED_DICTIONARY; + } + + inline bool IsJSNativePointer() const + { + return GetObjectType() == JSType::JS_NATIVE_POINTER; + } + + inline bool IsJSNativeObject() const + { + return GetObjectType() == JSType::JS_NATIVE_OBJECT; + } + + inline bool IsJSSymbol() const + { + return GetObjectType() == JSType::SYMBOL; + } + + inline bool IsJSArray() const + { + return GetObjectType() == JSType::JS_ARRAY; + } + + inline bool IsTypedArray() const + { + JSType jsType = GetObjectType(); + return (JSType::JS_TYPED_ARRAY_BEGIN < jsType && jsType <= JSType::JS_TYPED_ARRAY_END); + } + + inline bool IsJSTypedArray() const + { + return GetObjectType() == JSType::JS_TYPED_ARRAY; + } + + inline bool IsJSInt8Array() const + { + return GetObjectType() == JSType::JS_INT8_ARRAY; + } + + inline bool IsJSUint8Array() const + { + return GetObjectType() == JSType::JS_UINT8_ARRAY; + } + + inline bool IsJSUint8ClampedArray() const + { + return GetObjectType() == JSType::JS_UINT8_CLAMPED_ARRAY; + } + + inline bool IsJSInt16Array() const + { + return GetObjectType() == JSType::JS_INT16_ARRAY; + } + + inline bool IsJSUint16Array() const + { + return GetObjectType() == JSType::JS_UINT16_ARRAY; + } + + inline bool IsJSInt32Array() const + { + return GetObjectType() == JSType::JS_INT32_ARRAY; + } + + inline bool IsJSUint32Array() const + { + return GetObjectType() == JSType::JS_UINT32_ARRAY; + } + + inline bool IsJSFloat32Array() const + { + return GetObjectType() == JSType::JS_FLOAT32_ARRAY; + } + + inline bool IsJSFloat64Array() const + { + return GetObjectType() == JSType::JS_FLOAT64_ARRAY; + } + + inline bool IsJsGlobalEnv() const + { + return GetObjectType() == JSType::GLOBAL_ENV; + } + + inline bool IsJSFunctionBase() const + { + JSType jsType = GetObjectType(); + return jsType >= JSType::JS_FUNCTION_BASE && jsType <= JSType::JS_BOUND_FUNCTION; + } + + inline bool IsJsBoundFunction() const + { + return GetObjectType() == JSType::JS_BOUND_FUNCTION; + } + + inline bool IsJSProxyRevocFunction() const + { + return GetObjectType() == JSType::JS_PROXY_REVOC_FUNCTION; + } + + inline bool IsJSAsyncFunction() const + { + return GetObjectType() == JSType::JS_ASYNC_FUNCTION; + } + + inline bool IsJSAsyncAwaitStatusFunction() const + { + return GetObjectType() == JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION; + } + + inline bool IsJSPromiseReactionFunction() const + { + return GetObjectType() == JSType::JS_PROMISE_REACTIONS_FUNCTION; + } + + inline bool IsJSPromiseExecutorFunction() const + { + return GetObjectType() == JSType::JS_PROMISE_EXECUTOR_FUNCTION; + } + + inline bool IsJSPromiseAllResolveElementFunction() const + { + return GetObjectType() == JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION; + } + + inline bool IsJSFunctionExtraInfo() const + { + return GetObjectType() == JSType::FUNCTION_EXTRA_INFO; + } + + inline bool IsMicroJobQueue() const + { + return GetObjectType() == JSType::MICRO_JOB_QUEUE; + } + + inline bool IsPendingJob() const + { + return GetObjectType() == JSType::PENDING_JOB; + } + + inline bool IsJsPrimitiveRef() const + { + return GetObjectType() == JSType::JS_PRIMITIVE_REF; + }; + + bool IsJSSet() const + { + return GetObjectType() == JSType::JS_SET; + } + + bool IsJSMap() const + { + return GetObjectType() == JSType::JS_MAP; + } + + bool IsJSWeakMap() const + { + return GetObjectType() == JSType::JS_WEAK_MAP; + } + + bool IsJSWeakSet() const + { + return GetObjectType() == JSType::JS_WEAK_SET; + } + + bool IsJSFunction() const + { + return GetObjectType() >= JSType::JS_FUNCTION_BEGIN && GetObjectType() <= JSType::JS_FUNCTION_END; + } + + inline bool IsJSError() const + { + JSType jsType = GetObjectType(); + return jsType >= JSType::JS_ERROR_BEGIN && jsType <= JSType::JS_ERROR_END; + } + + inline bool IsArguments() const + { + return GetObjectType() == JSType::JS_ARGUMENTS; + } + + inline bool IsDate() const + { + return GetObjectType() == JSType::JS_DATE; + } + + inline bool IsJSRegExp() const + { + return GetObjectType() == JSType::JS_REG_EXP; + } + + inline bool IsJSProxy() const + { + return GetObjectType() == JSType::JS_PROXY; + } + + inline bool IsAccessorData() const + { + return GetObjectType() == JSType::ACCESSOR_DATA; + } + + inline bool IsInternalAccessor() const + { + return GetObjectType() == JSType::INTERNAL_ACCESSOR; + } + + inline bool IsIterator() const + { + JSType jsType = GetObjectType(); + return jsType >= JSType::JS_ITERATOR_BEGIN && jsType <= JSType::JS_ITERATOR_END; + } + + inline bool IsForinIterator() const + { + return GetObjectType() == JSType::JS_FORIN_ITERATOR; + } + + inline bool IsStringIterator() const + { + return GetObjectType() == JSType::JS_STRING_ITERATOR; + } + + inline bool IsArrayBuffer() const + { + return GetObjectType() == JSType::JS_ARRAY_BUFFER; + } + + inline bool IsDataView() const + { + return GetObjectType() == JSType::JS_DATA_VIEW; + } + + inline bool IsJSSetIterator() const + { + return GetObjectType() == JSType::JS_SET_ITERATOR; + } + + inline bool IsJSMapIterator() const + { + return GetObjectType() == JSType::JS_MAP_ITERATOR; + } + + inline bool IsJSArrayIterator() const + { + return GetObjectType() == JSType::JS_ARRAY_ITERATOR; + } + inline bool IsPrototypeHandler() const + { + return GetObjectType() == JSType::PROTOTYPE_HANDLER; + } + + inline bool IsTransitionHandler() const + { + return GetObjectType() == JSType::TRANSITION_HANDLER; + } + + inline bool IsPropertyBox() const + { + return GetObjectType() == JSType::PROPERTY_BOX; + } + inline bool IsProtoChangeMarker() const + { + return GetObjectType() == JSType::PROTO_CHANGE_MARKER; + } + + inline bool IsProtoChangeDetails() const + { + return GetObjectType() == JSType::PROTOTYPE_INFO; + } + + inline bool IsProgram() const + { + return GetObjectType() == JSType::PROGRAM; + } + + inline bool IsEcmaModule() const + { + return GetObjectType() == JSType::ECMA_MODULE; + } + + inline bool IsLexicalFunction() const + { + return GetObjectType() == JSType::LEXICAL_FUNCTION; + } + + inline bool IsCallable() const + { + uint64_t bits = GetBitField(); + return CallableBit::Decode(bits); + } + + inline bool IsConstructor() const + { + uint64_t bits = GetBitField(); + return ConstrutorBit::Decode(bits); + } + + inline bool IsBuiltinsCtor() const + { + uint64_t bits = GetBitField(); + return BuiltinsCtorBit::Decode(bits); + } + + inline bool IsExtensible() const + { + uint64_t bits = GetBitField(); + return ExtensibleBit::Decode(bits); + } + + inline bool IsPrototype() const + { + uint64_t bits = GetBitField(); + return IsPrototypeBit::Decode(bits); + } + + inline bool IsLiteral() const + { + uint64_t bits = GetBitField(); + return IsLiteralBit::Decode(bits); + } + + inline bool IsClassConstructor() const + { + uint64_t bits = GetBitField(); + return ClassConstructorBit::Decode(bits); + } + + inline bool IsJSGlobalObject() const + { + return GetObjectType() == JSType::JS_GLOBAL_OBJECT; + } + + inline bool IsClassPrototype() const + { + uint64_t bits = GetBitField(); + return ClassPrototypeBit::Decode(bits); + } + + inline bool IsDictionaryMode() const + { + JSTaggedType bits = GetBitField(); + return IsDictionaryBit::Decode(bits); + } + + inline bool IsObjectWrapper() const + { + return GetObjectType() == JSType::OBJECT_WRAPPER; + } + + inline bool IsGeneratorFunction() const + { + return GetObjectType() == JSType::JS_GENERATOR_FUNCTION; + } + + inline bool IsGeneratorObject() const + { + JSType jsType = GetObjectType(); + return jsType == JSType::JS_GENERATOR_OBJECT || jsType == JSType::JS_ASYNC_FUNC_OBJECT; + } + + inline bool IsAsyncFuncObject() const + { + return GetObjectType() == JSType::JS_ASYNC_FUNC_OBJECT; + } + + inline bool IsJSPromise() const + { + return GetObjectType() == JSType::JS_PROMISE; + } + + inline bool IsResolvingFunctionsRecord() const + { + return GetObjectType() == JSType::RESOLVING_FUNCTIONS_RECORD; + } + + inline bool IsPromiseRecord() const + { + return GetObjectType() == JSType::PROMISE_RECORD; + } + + inline bool IsPromiseIteratorRecord() const + { + return GetObjectType() == JSType::PROMISE_ITERATOR_RECORD; + } + + inline bool IsPromiseCapability() const + { + return GetObjectType() == JSType::PROMISE_CAPABILITY; + } + + inline bool IsPromiseReaction() const + { + return GetObjectType() == JSType::PROMISE_REACTIONS; + } + + inline bool IsCompletionRecord() const + { + return GetObjectType() == JSType::COMPLETION_RECORD; + } + + inline bool IsRecord() const + { + JSType jsType = GetObjectType(); + return jsType >= JSType::JS_RECORD_BEGIN && jsType <= JSType::JS_RECORD_END; + } + + inline bool IsTemplateMap() const + { + return GetObjectType() == JSType::TEMPLATE_MAP; + } + + inline bool IsFreeObjectWithOneField() const + { + return GetObjectType() == JSType::FREE_OBJECT_WITH_ONE_FIELD; + } + + inline bool IsFreeObjectWithNoneField() const + { + return GetObjectType() == JSType::FREE_OBJECT_WITH_NONE_FIELD; + } + + inline bool IsFreeObjectWithTwoField() const + { + return GetObjectType() == JSType::FREE_OBJECT_WITH_TWO_FIELD; + } + + inline void SetElementRepresentation(Representation representation) + { + uint64_t bits = GetBitField(); + uint64_t newVal = ElementRepresentationBits::Update(bits, representation); + SetBitField(newVal); + } + + inline Representation GetElementRepresentation() const + { + uint64_t bits = GetBitField(); + return ElementRepresentationBits::Decode(bits); + } + + inline void UpdateRepresentation(JSTaggedValue value) + { + Representation rep = PropertyAttributes::UpdateRepresentation(GetElementRepresentation(), value); + SetElementRepresentation(rep); + } + + inline void SetIsDictionaryElement(bool value) + { + JSTaggedType newVal = DictionaryElementBits::Update(GetBitField(), value); + SetBitField(newVal); + } + inline bool IsDictionaryElement() const + { + return DictionaryElementBits::Decode(GetBitField()); + } + inline void SetIsStableJSArray(bool value) + { + ASSERT(!value || (IsJSArray() && !IsDictionaryElement())); + JSTaggedType newVal = IsStableJSArrayBit::Update(GetBitField(), value); + SetBitField(newVal); + } + inline bool IsStableJSArray() const + { + ASSERT(!IsStableJSArrayBit::Decode(GetBitField()) || (IsJSArray() && !IsDictionaryElement())); + return IsStableJSArrayBit::Decode(GetBitField()); + } + inline void SetHasConstructor(bool value) + { + TaggedType newVal = HasConstructorBits::Update(GetBitField(), value); + SetBitField(newVal); + } + inline bool HasConstructor() const + { + return HasConstructorBits::Decode(GetBitField()); + } + inline uint32_t DecUnusedInlinedProps() + { + ASSERT(GetUnusedInlinedProps() != 0); + uint32_t num = GetUnusedInlinedProps() - 1; + SetUnusedInlinedProps(num); + return num; + } + + inline void SetUnusedInlinedProps(uint32_t num) + { + uint64_t bits = GetBitField(); + uint64_t newVal = NumberOfUnusedInlinedPropsBits::Update(bits, num); + SetBitField(newVal); + } + + inline uint32_t GetUnusedInlinedProps() const + { + uint64_t bits = GetBitField(); + return NumberOfUnusedInlinedPropsBits::Decode(bits); + } + + inline uint32_t DecUnusedNonInlinedProps() + { + ASSERT(GetUnusedNonInlinedProps() != 0); + uint32_t num = GetUnusedNonInlinedProps() - 1; + SetUnusedNonInlinedProps(num); + return num; + } + + inline void SetUnusedNonInlinedProps(uint32_t num) + { + uint64_t bits = GetBitField(); + uint64_t newVal = NumberOfUnusedNonInlinedPropsBits::Update(bits, num); + SetBitField(newVal); + } + + inline uint32_t GetUnusedNonInlinedProps() const + { + uint64_t bits = GetBitField(); + return NumberOfUnusedNonInlinedPropsBits::Decode(bits); + } + + inline int GetPropertiesNumber() const + { + return static_cast(PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES - GetUnusedNonInlinedProps() - + GetUnusedInlinedProps()); + } + + JSTaggedValue GetAccessor(const JSTaggedValue &key); + + static constexpr size_t BIT_FIELD_OFFSET = sizeof(TaggedObject) + sizeof(HClass); + SET_GET_PRIMITIVE_FIELD(BitField, uint64_t, BIT_FIELD_OFFSET, OBJECT_SIZE_OFFSET); + SET_GET_PRIMITIVE_FIELD(ObjectSize, uint64_t, OBJECT_SIZE_OFFSET, PROTOTYPE_OFFSET); + ACCESSORS(Proto, PROTOTYPE_OFFSET, ATTRIBUTES_OFFSET); + ACCESSORS(Attributes, ATTRIBUTES_OFFSET, TRANSTIONS_OFFSET); + ACCESSORS(Transitions, TRANSTIONS_OFFSET, PARENT_OFFSET); + ACCESSORS(Parent, PARENT_OFFSET, VALIDITY_CELL_OFFSET); + ACCESSORS(ProtoChangeMarker, VALIDITY_CELL_OFFSET, PROTOTYPE_INFO_OFFSET); + ACCESSORS(ProtoChangeDetails, PROTOTYPE_INFO_OFFSET, ENUM_CACHE_OFFSET); + ACCESSORS(EnumCache, ENUM_CACHE_OFFSET, SIZE); + + void SetPrototype(const JSThread *thread, JSTaggedValue proto); + void SetPrototype(const JSThread *thread, const JSHandle &proto); + inline JSTaggedValue GetPrototype() const + { + return GetProto(); + } + DECL_DUMP() + + static CString DumpJSType(JSType type); + + DECL_VISIT_OBJECT(PROTOTYPE_OFFSET, SIZE); + +private: + static inline void AddTransitions(const JSThread *thread, const JSHandle &parent, + const JSHandle &child, const JSHandle &key, + PropertyAttributes attr); + static inline void AddExtensionTransitions(const JSThread *thread, const JSHandle &parent, + const JSHandle &child, const JSHandle &key); + static inline void AddProtoTransitions(const JSThread *thread, const JSHandle &parent, + const JSHandle &child, const JSHandle &key, + const JSHandle &proto); + inline JSHClass *FindTransitions(const JSTaggedValue &key, const JSTaggedValue &attributes); + inline JSHClass *FindProtoTransitions(const JSTaggedValue &key, const JSTaggedValue &proto); + + static void CopyAll(const JSThread *thread, const JSHandle &newjshclass, + const JSHandle &jshclass); + inline void Copy(const JSThread *thread, const JSHClass *jshcalss); + + JSTaggedType *GetBitFieldAddr() const + { + return reinterpret_cast(ToUintPtr(this) + BIT_FIELD_OFFSET); + } +}; +static_assert(JSHClass::BIT_FIELD_OFFSET % static_cast(MemAlignment::MEM_ALIGN_OBJECT) == 0); +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_HCLASS_H diff --git a/ecmascript/js_int16_array.h b/ecmascript/js_int16_array.h new file mode 100644 index 0000000000000000000000000000000000000000..66e9bd0b48eca3ae68bfd6063b859d401437a462 --- /dev/null +++ b/ecmascript/js_int16_array.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_INT16_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_INT16_ARRAY_H + +#include +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class JSInt16Array : public JSObject { +public: + static JSInt16Array *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSInt16Array()); + return static_cast(object); + } + + static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; + ACCESSORS(ViewedArrayBuffer, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) + ACCESSORS(TypedArrayName, TYPED_ARRAY_NAME_OFFSET, BYTE_LENGTH_OFFSET) + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET) + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, ARRAY_LENGTH_OFFSET) + ACCESSORS(ArrayLength, ARRAY_LENGTH_OFFSET, SIZE) + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VIEWED_ARRAY_BUFFER_OFFSET, SIZE) +}; // namespace panda::ecmascript +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_INT16_ARRAY_H diff --git a/ecmascript/js_int32_array.h b/ecmascript/js_int32_array.h new file mode 100644 index 0000000000000000000000000000000000000000..8bc079de03c13094549a5f9b8ad3451052c6f923 --- /dev/null +++ b/ecmascript/js_int32_array.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_INT32_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_INT32_ARRAY_H + +#include +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class JSInt32Array : public JSObject { +public: + static JSInt32Array *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSInt32Array()); + return static_cast(object); + } + + static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; + ACCESSORS(ViewedArrayBuffer, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) + ACCESSORS(TypedArrayName, TYPED_ARRAY_NAME_OFFSET, BYTE_LENGTH_OFFSET) + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET) + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, ARRAY_LENGTH_OFFSET) + ACCESSORS(ArrayLength, ARRAY_LENGTH_OFFSET, SIZE) + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VIEWED_ARRAY_BUFFER_OFFSET, SIZE) +}; // namespace panda::ecmascript +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_INT32_ARRAY_H diff --git a/ecmascript/js_int8_array.h b/ecmascript/js_int8_array.h new file mode 100644 index 0000000000000000000000000000000000000000..af3a64718a36bead370cd3eb44cf777d1f342853 --- /dev/null +++ b/ecmascript/js_int8_array.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_INT8_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_INT8_ARRAY_H + +#include +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class JSInt8Array : public JSObject { +public: + static JSInt8Array *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSInt8Array()); + return static_cast(object); + } + + static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; + ACCESSORS(ViewedArrayBuffer, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) + ACCESSORS(TypedArrayName, TYPED_ARRAY_NAME_OFFSET, BYTE_LENGTH_OFFSET) + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET) + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, ARRAY_LENGTH_OFFSET) + ACCESSORS(ArrayLength, ARRAY_LENGTH_OFFSET, SIZE) + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VIEWED_ARRAY_BUFFER_OFFSET, SIZE) +}; // namespace panda::ecmascript +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_INT8_ARRAY_H diff --git a/ecmascript/js_invoker.cpp b/ecmascript/js_invoker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..65d82b939cb43809a1e90f7dd5eeeccb10b8e832 --- /dev/null +++ b/ecmascript/js_invoker.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_invoker.h" + +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/interpreter/interpreter-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/tagged_array-inl.h" +#include "libpandabase/utils/span.h" + +namespace panda::ecmascript { +JSTaggedValue JsInvoker::Invoke(JSThread *thread) +{ + UNREACHABLE(); +} + +JSTaggedValue InvokeJsFunction(JSThread *thread, const JSHandle &func, const JSHandle &obj, + const JSHandle &newTgt, const JSHandle &args) +{ + ASSERT(func->GetCallTarget() != nullptr); + + CallParams params; + params.callTarget = ECMAObject::Cast(*func); + params.newTarget = newTgt.GetTaggedType(); + params.thisArg = obj.GetTaggedType(); + params.argc = args->GetLength(); + CVector values; + values.reserve(params.argc); + + for (uint32_t i = 0; i < params.argc; ++i) { + JSTaggedValue arg = args->Get(thread, i); + values.emplace_back(arg.GetRawData()); + } + + params.argv = values.data(); + return EcmaInterpreter::Execute(thread, params); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_invoker.h b/ecmascript/js_invoker.h new file mode 100644 index 0000000000000000000000000000000000000000..e31546279394977cb05758a5b8ed53ae5291c4e6 --- /dev/null +++ b/ecmascript/js_invoker.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_INVOKE_H +#define PANDA_RUNTIME_ECMASCRIPT_INVOKE_H + +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_object.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/c_containers.h" + +namespace panda::ecmascript { +class JsInvoker { +public: + JsInvoker(const JSThread *thread, JSTaggedValue func, JSTaggedValue obj) + : JsInvoker(thread, func, obj, JSTaggedValue::Undefined()) + { + } + + JsInvoker(const JSThread *thread, JSTaggedValue func, JSTaggedValue obj, JSTaggedValue newTarget) + { + AddArgument(JSHandle(thread, func)); + AddArgument(JSHandle(thread, newTarget)); + AddArgument(JSHandle(thread, obj)); + } + + ~JsInvoker() = default; + NO_COPY_SEMANTIC(JsInvoker); + NO_MOVE_SEMANTIC(JsInvoker); + + template + void AddArgument(const JSHandle &arg) + { + args_.emplace_back(JSHandle(arg)); + } + + template + void AddArgument(JSHandle &&arg) + { + args_.emplace_back(std::move(arg)); + } + + JSTaggedValue Invoke(JSThread *thread); + +private: + CVector> args_{}; +}; + +JSTaggedValue InvokeJsFunction(JSThread *thread, const JSHandle &func, const JSHandle &obj, + const JSHandle &newTgt, const JSHandle &args); +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_INVOKE_H diff --git a/ecmascript/js_iterator.cpp b/ecmascript/js_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..191a3bb6743cbf629d0e2ce56753b1119bf6be01 --- /dev/null +++ b/ecmascript/js_iterator.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_iterator.h" +#include "ecma_macros.h" +#include "ecma_vm.h" +#include "ecmascript/accessor_data.h" +#include "global_env.h" +#include "js_invoker.h" +#include "js_symbol.h" +#include "object_factory.h" + +namespace panda::ecmascript { +JSTaggedValue JSIterator::IteratorCloseAndReturn(JSThread *thread, const JSHandle &iter, + const JSHandle &status) +{ + ASSERT(thread->HasPendingException()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle record; + if (thread->GetException().IsObjectWrapper()) { + JSTaggedValue exception = ObjectWrapper::Cast(thread->GetException().GetTaggedObject())->GetValue(); + record = JSHandle(factory->NewCompletionRecord(CompletionRecord::THROW, + JSHandle(thread, exception))); + } else { + record = + JSHandle(factory->NewCompletionRecord(CompletionRecord::NORMAL, status)); + } + JSHandle result = JSIterator::IteratorClose(thread, iter, record); + if (result->IsCompletionRecord()) { + return CompletionRecord::Cast(result->GetTaggedObject())->GetValue(); + } + return result.GetTaggedValue(); +} + +JSHandle JSIterator::GetIterator(JSThread *thread, const JSHandle &obj) +{ + // 1.ReturnIfAbrupt(obj). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj); + // 2.If method was not passed, then + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle func = JSObject::GetMethod(thread, obj, iteratorSymbol); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj); + + return GetIterator(thread, obj, func); +} + +JSHandle JSIterator::GetIterator(JSThread *thread, const JSHandle &obj, + const JSHandle &method) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1.ReturnIfAbrupt(obj). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj); + // 3.Let iterator be Call(method,obj). + JSHandle array(factory->EmptyArray()); + JSTaggedValue ret = JSFunction::Call(thread, method, obj, array); + JSHandle iter(thread, ret); + // 4.ReturnIfAbrupt(iterator). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter); + // 5.If Type(iterator) is not Object, throw a TypeError exception + if (!iter->IsECMAObject()) { + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + THROW_TYPE_ERROR_AND_RETURN(thread, "", undefinedHandle); + } + return iter; +} // namespace panda::ecmascript +// 7.4.2 +JSHandle JSIterator::IteratorNext(JSThread *thread, const JSHandle &iter) +{ + // 1.If value was not passed, then Let result be Invoke(iterator, "next", «‍ »). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle argv(factory->EmptyArray()); + JSHandle key(thread->GlobalConstants()->GetHandledNextString()); + JSHandle next(JSObject::GetMethod(thread, iter, key)); + ASSERT(next->IsCallable()); + JSTaggedValue ret = JSFunction::Call(thread, next, iter, argv); + JSHandle result(thread, ret); + // 3.ReturnIfAbrupt(result) + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result); + // 4.If Type(result) is not Object, throw a TypeError exception. + if (!result->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", result); + } + return result; +} + +JSHandle JSIterator::IteratorNext(JSThread *thread, const JSHandle &iter, + const JSHandle &value) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 2.Let result be Invoke(iterator, "next", «‍value»). + JSHandle argv(factory->NewTaggedArray(1)); + argv->Set(thread, 0, value); + JSHandle key(thread->GlobalConstants()->GetHandledNextString()); + JSHandle next(JSObject::GetMethod(thread, iter, key)); + ASSERT(next->IsCallable()); + JSTaggedValue ret = JSFunction::Call(thread, next, iter, argv); + JSHandle result(thread, ret); + // 3.ReturnIfAbrupt(result) + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result); + // 4.If Type(result) is not Object, throw a TypeError exception. + if (!result->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", result); + } + return result; +} +// 7.4.3 +bool JSIterator::IteratorComplete(JSThread *thread, const JSHandle &iterResult) +{ + ASSERT_PRINT(iterResult->IsECMAObject(), "iterResult must be JSObject"); + // Return ToBoolean(Get(iterResult, "done")). + JSHandle doneStr = thread->GlobalConstants()->GetHandledDoneString(); + JSHandle done = JSTaggedValue::GetProperty(thread, iterResult, doneStr).GetValue(); + return done->ToBoolean(); +} +// 7.4.4 +JSHandle JSIterator::IteratorValue(JSThread *thread, const JSHandle &iterResult) +{ + ASSERT_PRINT(iterResult->IsECMAObject(), "iterResult must be JSObject"); + // Return Get(iterResult, "value"). + JSHandle valueStr = thread->GlobalConstants()->GetHandledValueString(); + JSHandle value = JSTaggedValue::GetProperty(thread, iterResult, valueStr).GetValue(); + return value; +} +// 7.4.5 +JSHandle JSIterator::IteratorStep(JSThread *thread, const JSHandle &iter) +{ + // 1.Let result be IteratorNext(iterator). + JSHandle result(IteratorNext(thread, iter)); + // 2.ReturnIfAbrupt(result). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result); + // 3.Let done be IteratorComplete(result). + bool done = IteratorComplete(thread, result); + // 4.ReturnIfAbrupt(done). + JSHandle doneHandle(thread, JSTaggedValue(done)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, doneHandle); + // 5.If done is true, return false. + if (done) { + JSHandle falseHandle(thread, JSTaggedValue::False()); + return falseHandle; + } + return result; +} +// 7.4.6 +JSHandle JSIterator::IteratorClose(JSThread *thread, const JSHandle &iter, + const JSHandle &completion) +{ + // 1.Assert: Type(iterator) is Object. + ASSERT_PRINT(iter->IsECMAObject(), "iter must be JSObject"); + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle exceptionOnThread; + if (thread->HasPendingException()) { + exceptionOnThread = JSHandle(thread, thread->GetException()); + thread->ClearException(); + } + JSHandle returnStr(globalConst->GetHandledReturnString()); + // 3.Let return be GetMethod(iterator, "return"). + JSHandle returnFunc(JSObject::GetMethod(thread, iter, returnStr)); + // 4.ReturnIfAbrupt(return). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, returnFunc); + // 5.If return is undefined, return Completion(completion). + if (returnFunc->IsUndefined()) { + if (!exceptionOnThread.IsEmpty()) { + thread->SetException(exceptionOnThread.GetTaggedValue()); + } + return completion; + } + // 6.Let innerResult be Call(return, iterator, «‍ »). + JSTaggedValue ret = JSFunction::Call(thread, returnFunc, iter, factory->EmptyArray()); + if (!exceptionOnThread.IsEmpty()) { + thread->SetException(exceptionOnThread.GetTaggedValue()); + } + JSHandle innerResult(thread, ret); + JSHandle completionRecord(thread, globalConst->GetUndefined()); + if (completion->IsCompletionRecord()) { + completionRecord = JSHandle::Cast(completion); + } + // 7.If completion.[[type]] is throw, return Completion(completion). + if (!completionRecord.GetTaggedValue().IsUndefined() && completionRecord->IsThrow()) { + if (!exceptionOnThread.IsEmpty()) { + thread->SetException(exceptionOnThread.GetTaggedValue()); + } + return completion; + } + // 8.If innerResult.[[type]] is throw, return Completion(innerResult). + if (thread->HasPendingException()) { + return innerResult; + } + // 9.If Type(innerResult.[[value]]) is not Object, throw a TypeError exception. + if (!innerResult->IsECMAObject()) { + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + THROW_TYPE_ERROR_AND_RETURN(thread, "", undefinedHandle); + } + if (!exceptionOnThread.IsEmpty()) { + thread->SetException(exceptionOnThread.GetTaggedValue()); + } + return completion; +} +// 7.4.7 +JSHandle JSIterator::CreateIterResultObject(JSThread *thread, const JSHandle &value, bool done) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + auto globalConst = thread->GlobalConstants(); + JSHandle constructor(env->GetObjectFunction()); + JSHandle valueStr = globalConst->GetHandledValueString(); + JSHandle doneStr = globalConst->GetHandledDoneString(); + JSHandle doneValue(thread, JSTaggedValue(done)); + // 2. Let obj be OrdinaryObjectCreate(%Object.prototype%). + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + // 3. Perform ! CreateDataPropertyOrThrow(obj, "value", value). + // 4. Perform ! CreateDataPropertyOrThrow(obj, "done", done). + JSObject::CreateDataPropertyOrThrow(thread, obj, valueStr, value); + JSObject::CreateDataPropertyOrThrow(thread, obj, doneStr, doneValue); + ASSERT_NO_ABRUPT_COMPLETION(thread); + // 5. Return obj. + return obj; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_iterator.h b/ecmascript/js_iterator.h new file mode 100644 index 0000000000000000000000000000000000000000..5c3c733753d0eefe3cd6227c15cc87fde2072dde --- /dev/null +++ b/ecmascript/js_iterator.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_ITERATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_ITERATOR_H + +#include "ecmascript/js_tagged_value.h" +#include "tagged_array-inl.h" + +namespace panda::ecmascript { +enum class IterationKind { KEY = 0, VALUE, KEY_AND_VALUE }; +class JSIterator final { +public: + static JSTaggedValue IteratorCloseAndReturn(JSThread *thread, const JSHandle &iter, + const JSHandle &status); + // 7.4.1 + static JSHandle GetIterator(JSThread *thread, const JSHandle &obj); + + static JSHandle GetIterator(JSThread *thread, const JSHandle &obj, + const JSHandle &method); + // 7.4.2 + static JSHandle IteratorNext(JSThread *thread, const JSHandle &iter, + const JSHandle &value); + + static JSHandle IteratorNext(JSThread *thread, const JSHandle &iter); + // 7.4.3 + static bool IteratorComplete(JSThread *thread, const JSHandle &iterResult); + // 7.4.4 + static JSHandle IteratorValue(JSThread *thread, const JSHandle &iterResult); + // 7.4.5 + static JSHandle IteratorStep(JSThread *thread, const JSHandle &iter); + // 7.4.6 + static JSHandle IteratorClose(JSThread *thread, const JSHandle &iter, + const JSHandle &completion); + // 7.4.7 + static JSHandle CreateIterResultObject(JSThread *thread, const JSHandle &value, bool done); +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_ITERATOR_H diff --git a/ecmascript/js_map.cpp b/ecmascript/js_map.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9dc01305388be3d4faf06dd9f0a334579b77311f --- /dev/null +++ b/ecmascript/js_map.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_tagged_value.h" +#include "js_map.h" +#include "linked_hash_table-inl.h" +#include "object_factory.h" +#include "utils/bit_utils.h" + +namespace panda::ecmascript { +void JSMap::Set(JSThread *thread, const JSHandle &map, const JSHandle &key, + const JSHandle &value) +{ + if (!LinkedHashMap::IsKey(key.GetTaggedValue())) { + THROW_TYPE_ERROR(thread, "the value must be Key of JSSet"); + } + JSHandle mapHandle(thread, LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject())); + + auto result = LinkedHashMap::Set(thread, mapHandle, key, value); + map->SetLinkedMap(thread, result); +} + +bool JSMap::Delete(const JSThread *thread, const JSHandle &map, const JSHandle &key) +{ + JSHandle mapHandle(thread, LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject())); + int entry = mapHandle->FindElement(key.GetTaggedValue()); + if (entry == -1) { + return false; + } + mapHandle->RemoveEntry(thread, entry); + + auto result = LinkedHashMap::Shrink(thread, mapHandle); + map->SetLinkedMap(thread, result); + return true; +} + +void JSMap::Clear(const JSThread *thread, const JSHandle &map) +{ + LinkedHashMap *linkedMap = LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject()); + linkedMap->Clear(thread); +} + +bool JSMap::Has(JSTaggedValue key) const +{ + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->Has(key); +} + +JSTaggedValue JSMap::Get(JSTaggedValue key) const +{ + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->Get(key); +} + +int JSMap::GetSize() const +{ + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->NumberOfElements(); +} + +JSTaggedValue JSMap::GetKey(int entry) const +{ + ASSERT_PRINT(entry >= 0 && entry < GetSize(), "entry must be non-negative integer less than capacity"); + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->GetKey(entry); +} + +JSTaggedValue JSMap::GetValue(int entry) const +{ + ASSERT_PRINT(entry >= 0 && entry < GetSize(), "entry must be non-negative integer less than capacity"); + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->GetValue(entry); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_map.h b/ecmascript/js_map.h new file mode 100644 index 0000000000000000000000000000000000000000..580c58fdbdb838d8968942512e11d8aca4d63591 --- /dev/null +++ b/ecmascript/js_map.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSMAP_H +#define PANDA_RUNTIME_ECMASCRIPT_JSMAP_H + +#include +#include "js_object.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +class JSMap : public JSObject { +public: + static JSMap *Cast(ObjectHeader *object) + { + return static_cast(object); + } + static bool Delete(const JSThread *thread, const JSHandle &map, const JSHandle &key); + + static void Set(JSThread *thread, const JSHandle &map, const JSHandle &key, + const JSHandle &value); + static void Clear(const JSThread *thread, const JSHandle &map); + + bool Has(JSTaggedValue key) const; + + JSTaggedValue Get(JSTaggedValue key) const; + + int GetSize() const; + + JSTaggedValue GetKey(int entry) const; + + JSTaggedValue GetValue(int entry) const; + + static constexpr size_t LINKED_MAP_OFFSET = JSObject::SIZE; + ACCESSORS(LinkedMap, LINKED_MAP_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LINKED_MAP_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSMAP_H diff --git a/ecmascript/js_map_iterator.cpp b/ecmascript/js_map_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3ba579cec28f95900706558042936e18c3143f45 --- /dev/null +++ b/ecmascript/js_map_iterator.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_map_iterator.h" +#include "builtins/builtins_errors.h" +#include "js_array.h" +#include "js_map.h" +#include "linked_hash_table-inl.h" +#include "object_factory.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +JSTaggedValue JSMapIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.Let O be the this value + JSHandle input(BuiltinsBase::GetThis(argv)); + + // 3.If O does not have all of the internal slots of a Map Iterator Instance (23.1.5.3), throw a TypeError + // exception. + if (!input->IsJSMapIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not a map iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + iter->Update(thread); + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + // 4.Let m be O.[[IteratedMap]]. + JSHandle iteratedMap(thread, iter->GetIteratedMap()); + + // 5.Let index be O.[[MapNextIndex]]. + int index = iter->GetNextIndex().GetInt(); + IterationKind itemKind = IterationKind(iter->GetIterationKind().GetInt()); + // 7.If m is undefined, return CreateIterResultObject(undefined, true). + if (iteratedMap->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + }; + JSHandle map(iteratedMap); + int totalElements = map->NumberOfElements() + map->NumberOfDeletedElements(); + + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + while (index < totalElements) { + JSTaggedValue key = map->GetKey(index); + if (!key.IsHole()) { + iter->SetNextIndex(thread, JSTaggedValue(index + 1)); + keyHandle.Update(key); + // If itemKind is key, let result be e.[[Key]] + if (itemKind == IterationKind::KEY) { + return JSIterator::CreateIterResultObject(thread, keyHandle, false).GetTaggedValue(); + } + JSHandle value(thread, map->GetValue(index)); + // Else if itemKind is value, let result be e.[[Value]]. + if (itemKind == IterationKind::VALUE) { + return JSIterator::CreateIterResultObject(thread, value, false).GetTaggedValue(); + } + // Else + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle array(factory->NewTaggedArray(2)); // 2 means the length of array + array->Set(thread, 0, keyHandle); + array->Set(thread, 1, value); + JSHandle keyAndValue(JSArray::CreateArrayFromList(thread, array)); + return JSIterator::CreateIterResultObject(thread, keyAndValue, false).GetTaggedValue(); + } + index++; + } + // 13.Set O.[[IteratedMap]] to undefined. + iter->SetIteratedMap(thread, JSTaggedValue::Undefined()); + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); +} + +void JSMapIterator::Update(const JSThread *thread) +{ + [[maybe_unused]] DisallowGarbageCollection noGc; + JSTaggedValue iteratedMap = GetIteratedMap(); + if (iteratedMap.IsUndefined()) { + return; + } + LinkedHashMap *map = LinkedHashMap::Cast(iteratedMap.GetTaggedObject()); + if (map->GetNextTable().IsHole()) { + return; + } + int index = GetNextIndex().GetInt(); + JSTaggedValue nextTable = map->GetNextTable(); + while (!nextTable.IsHole()) { + index -= map->GetDeletedElementsAt(index); + map = LinkedHashMap::Cast(nextTable.GetTaggedObject()); + nextTable = map->GetNextTable(); + } + SetIteratedMap(thread, JSTaggedValue(map)); + SetNextIndex(thread, JSTaggedValue(index)); +} + +JSHandle JSMapIterator::CreateMapIterator(JSThread *thread, const JSHandle &obj, + IterationKind kind) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (!obj->IsJSMap()) { + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", undefinedHandle); + } + JSHandle iter(factory->NewJSMapIterator(JSHandle(obj), kind)); + return iter; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_map_iterator.h b/ecmascript/js_map_iterator.h new file mode 100644 index 0000000000000000000000000000000000000000..d596a0a966d7a74e0cd1f94582d8b3c5ed226d03 --- /dev/null +++ b/ecmascript/js_map_iterator.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_MAP_ITERATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_MAP_ITERATOR_H + +#include "js_iterator.h" +#include "js_object.h" +namespace panda::ecmascript { +class JSMapIterator : public JSObject { +public: + static JSMapIterator *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsJSMapIterator()); + return static_cast(obj); + } + static JSHandle CreateMapIterator(JSThread *thread, const JSHandle &obj, + IterationKind kind); + + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + void Update(const JSThread *thread); + + static constexpr size_t ITERATED_MAP_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedMap, ITERATED_MAP_OFFSET, NEXT_INDEX_OFFSET); + ACCESSORS(NextIndex, NEXT_INDEX_OFFSET, ITERATION_KIND_OFFSET); + ACCESSORS(IterationKind, ITERATION_KIND_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_MAP_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_MAP_ITERATOR_H diff --git a/ecmascript/js_method.h b/ecmascript/js_method.h new file mode 100644 index 0000000000000000000000000000000000000000..7429cb381f6e61a3b68f35d03285a7ccf3766f48 --- /dev/null +++ b/ecmascript/js_method.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_METHOD_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_METHOD_H + +#include "include/method.h" +#include "libpandafile/file.h" + +namespace panda { +class Class; +} + +namespace panda::ecmascript { +class JSMethod : public Method { +public: + static JSMethod *Cast(Method *method) + { + return static_cast(method); + } + + explicit JSMethod(Class *klass, const panda_file::File *pf, panda_file::File::EntityId fileId, + panda_file::File::EntityId codeId, uint32_t accessFlags, uint32_t numArgs, const uint16_t *shorty) + : Method(klass, pf, fileId, codeId, accessFlags, numArgs, shorty) + { + bytecodeArray_ = JSMethod::GetInstructions(); + bytecodeArraySize_ = JSMethod::GetCodeSize(); + } + + JSMethod() = delete; + JSMethod(const JSMethod &) = delete; + JSMethod(JSMethod &&) = delete; + JSMethod &operator=(const JSMethod &) = delete; + JSMethod &operator=(JSMethod &&) = delete; + + static constexpr uint32_t GetBytecodeArrayOffset() + { + return MEMBER_OFFSET(JSMethod, bytecodeArray_); + } + + const uint8_t *GetBytecodeArray() const + { + return bytecodeArray_; + } + + uint32_t GetBytecodeArraySize() const + { + return bytecodeArraySize_; + } + + void SetBytecodeArray(const uint8_t *bc) + { + bytecodeArray_ = bc; + } + +private: + const uint8_t *bytecodeArray_ {nullptr}; + uint32_t bytecodeArraySize_ {0}; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_METHOD_H diff --git a/ecmascript/js_native_object.h b/ecmascript/js_native_object.h new file mode 100644 index 0000000000000000000000000000000000000000..2c4e48fe4d6f86ad13aef9d71b5ac4a705854192 --- /dev/null +++ b/ecmascript/js_native_object.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSNATIVEOBJECT_H +#define PANDA_RUNTIME_ECMASCRIPT_JSNATIVEOBJECT_H + +#include "js_object.h" +#include "js_native_pointer.h" + +namespace panda::ecmascript { +class JSNativeObject : JSObject { +public: + static JSNativeObject *Cast(TaggedObject *object) + { + ASSERT(JSTaggedValue(object).IsJSNativeObject()); + return static_cast(object); + } + + inline void SetDeleter(DeleteEntryPoint deleter) + { + if (!GetJSNativePointer().IsJSNativePointer()) { + return; + } + JSNativePointer::Cast(GetJSNativePointer().GetHeapObject())->SetDeleter(deleter); + } + + inline void SetData(void *data) + { + if (!GetJSNativePointer().IsJSNativePointer()) { + return; + } + JSNativePointer::Cast(GetJSNativePointer().GetHeapObject())->SetData(data); + } + + inline const void *GetData() const + { + if (!GetJSNativePointer().IsJSNativePointer()) { + return nullptr; + } + return JSNativePointer::Cast(GetJSNativePointer().GetHeapObject())->GetData(); + } + + inline void Destroy() + { + if (!GetJSNativePointer().IsJSNativePointer()) { + return; + } + JSNativePointer::Cast(GetJSNativePointer().GetHeapObject())->Destroy(); + } + + inline void *GetExternalPointer() const + { + if (!GetJSNativePointer().IsJSNativePointer()) { + return nullptr; + } + return JSNativePointer::Cast(GetJSNativePointer().GetHeapObject())->GetExternalPointer(); + } + + static constexpr size_t OBJECT_OFFSET = JSObject::SIZE; + ACCESSORS(JSNativePointer, OBJECT_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, OBJECT_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSNATIVEOBJECT_H diff --git a/ecmascript/js_native_pointer.h b/ecmascript/js_native_pointer.h new file mode 100644 index 0000000000000000000000000000000000000000..f7d511b06328e2722a9fc5d514ce20897b7158bf --- /dev/null +++ b/ecmascript/js_native_pointer.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSNATIVEPOINTER_H +#define PANDA_RUNTIME_ECMASCRIPT_JSNATIVEPOINTER_H + +#include "include/coretypes/native_pointer.h" + +namespace panda::ecmascript { +using DeleteEntryPoint = void (*)(void *, void *); + +class JSNativePointer : public coretypes::NativePointer { +public: + static JSNativePointer *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSNativePointer()); + return reinterpret_cast(object); + } + + inline void SetDeleter(DeleteEntryPoint deleter) + { + deleter_ = deleter; + } + + inline void SetData(void *data) + { + data_ = data; + } + + inline const void *GetData() const + { + return data_; + } + + inline void Destroy() + { + if (deleter_ == nullptr || GetExternalPointer() == nullptr) { + return; + } + deleter_(GetExternalPointer(), data_); + SetExternalPointer(nullptr); + } + +private: + DeleteEntryPoint deleter_{nullptr}; + void *data_{nullptr}; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSNATIVEPOINTER_H diff --git a/ecmascript/js_object-inl.h b/ecmascript/js_object-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..56e59071c08bebff6d6a778fca6d10b12c834fa7 --- /dev/null +++ b/ecmascript/js_object-inl.h @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSOBJECT_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_JSOBJECT_INL_H + +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript { +inline ECMAObject *ECMAObject::Cast(ObjectHeader *object) +{ + ASSERT(JSTaggedValue(object).IsECMAObject()); + return static_cast(object); +} + +inline void ECMAObject::SetBuiltinsCtorMode() +{ + GetClass()->SetBuiltinsCtor(true); +} + +inline bool ECMAObject::IsBuiltinsConstructor() const +{ + return GetClass()->IsBuiltinsCtor(); +} + +inline void ECMAObject::SetCallable(bool flag) +{ + GetClass()->SetCallable(flag); +} + +inline bool ECMAObject::IsCallable() const +{ + return GetClass()->IsCallable(); +} + +// JSObject +inline bool JSObject::IsExtensible() const +{ + return GetJSHClass()->IsExtensible(); +} + +inline void JSObject::FillElementsWithHoles(const JSThread *thread, uint32_t start, uint32_t end) +{ + if (start >= end) { + return; + } + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + for (uint32_t i = start; i < end; i++) { + elements->Set(thread, i, JSTaggedValue::Hole()); + } +} + +inline JSHClass *JSObject::GetJSHClass() const +{ + return GetClass(); +} + +inline bool JSObject::IsJSGlobalObject() const +{ + return GetJSHClass()->IsJSGlobalObject(); +} + +inline bool JSObject::IsConstructor() const +{ + return GetJSHClass()->IsConstructor(); +} + +inline bool JSObject::IsECMAObject() const +{ + return GetJSHClass()->IsECMAObject(); +} + +inline bool JSObject::IsJSError() const +{ + return GetJSHClass()->IsJSError(); +} + +inline bool JSObject::IsArguments() const +{ + return GetJSHClass()->IsArguments(); +} + +inline bool JSObject::IsDate() const +{ + return GetJSHClass()->IsDate(); +} + +inline bool JSObject::IsJSArray() const +{ + return GetJSHClass()->IsJSArray(); +} + +inline bool JSObject::IsJSMap() const +{ + return GetJSHClass()->IsJSMap(); +} + +inline bool JSObject::IsJSSet() const +{ + return GetJSHClass()->IsJSSet(); +} + +inline bool JSObject::IsJSRegExp() const +{ + return GetJSHClass()->IsJSRegExp(); +} + +inline bool JSObject::IsJSFunction() const +{ + return GetJSHClass()->IsJSFunction(); +} + +inline bool JSObject::IsBoundFunction() const +{ + return GetJSHClass()->IsJsBoundFunction(); +} + +inline bool JSObject::IsProxyRevocFunction() const +{ + return GetJSHClass()->IsJSProxyRevocFunction(); +} + +inline bool JSObject::IsAccessorData() const +{ + return GetJSHClass()->IsAccessorData(); +} + +inline bool JSObject::IsJSGlobalEnv() const +{ + return GetJSHClass()->IsJsGlobalEnv(); +} + +inline bool JSObject::IsJSProxy() const +{ + return GetJSHClass()->IsJSProxy(); +} + +inline bool JSObject::IsGeneratorObject() const +{ + return GetJSHClass()->IsGeneratorObject(); +} + +inline bool JSObject::IsForinIterator() const +{ + return GetJSHClass()->IsForinIterator(); +} + +inline bool JSObject::IsJSSetIterator() const +{ + return GetJSHClass()->IsJSSetIterator(); +} + +inline bool JSObject::IsJSMapIterator() const +{ + return GetJSHClass()->IsJSMapIterator(); +} + +inline bool JSObject::IsJSArrayIterator() const +{ + return GetJSHClass()->IsJSArrayIterator(); +} + +inline bool JSObject::IsJSPrimitiveRef() const +{ + return GetJSHClass()->IsJsPrimitiveRef(); +} + +inline bool JSObject::IsElementDict() const +{ + return TaggedArray::Cast(GetElements().GetTaggedObject())->IsDictionaryMode(); +} + +inline bool JSObject::IsPropertiesDict() const +{ + return TaggedArray::Cast(GetProperties().GetTaggedObject())->IsDictionaryMode(); +} + +inline bool JSObject::IsTypedArray() const +{ + return GetJSHClass()->IsTypedArray(); +} + +void JSObject::SetPropertyInlinedProps(const JSThread *thread, uint32_t index, JSTaggedValue value) +{ + SetPropertyInlinedProps(thread, GetJSHClass(), index, value); +} + +JSTaggedValue JSObject::GetPropertyInlinedProps(uint32_t index) const +{ + return GetPropertyInlinedProps(GetJSHClass(), index); +} + +void JSObject::SetPropertyInlinedProps(const JSThread *thread, const JSHClass *hclass, uint32_t index, + JSTaggedValue value) +{ + ASSERT(index < JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + size_t offset = + hclass->GetObjectSize() - (JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS - index) * JSTaggedValue::TaggedTypeSize(); + SET_VALUE_WITH_BARRIER(thread, this, offset, value); +} + +JSTaggedValue JSObject::GetPropertyInlinedProps(const JSHClass *hclass, uint32_t index) const +{ + ASSERT(index < JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + size_t offset = + hclass->GetObjectSize() - (JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS - index) * JSTaggedValue::TaggedTypeSize(); + return JSTaggedValue(GET_VALUE(this, offset)); +} + +JSTaggedValue JSObject::GetProperty(const JSHClass *hclass, PropertyAttributes attr) const +{ + if (attr.IsInlinedProps()) { + return GetPropertyInlinedProps(hclass, attr.GetOffset()); + } + TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); + return array->Get(attr.GetOffset() - JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); +} + +void JSObject::SetProperty(const JSThread *thread, const JSHClass *hclass, PropertyAttributes attr, JSTaggedValue value) +{ + if (attr.IsInlinedProps()) { + SetPropertyInlinedProps(thread, hclass, attr.GetOffset(), value); + } else { + TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); + array->Set(thread, attr.GetOffset() - JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS, value); + } +} + +inline bool JSObject::ShouldTransToDict(uint32_t capacity, uint32_t index) +{ + if (index < capacity) { + return false; + } + if (index - capacity > MAX_GAP) { + return true; + } + + if (capacity >= MIN_GAP) { + return index > capacity * FAST_ELEMENTS_FACTOR; + } + + return false; +} + +inline uint32_t JSObject::ComputeElementCapacity(uint32_t oldCapacity) +{ + uint32_t newCapacity = oldCapacity + (oldCapacity >> 1U); + return newCapacity > MIN_ELEMENTS_LENGTH ? newCapacity : MIN_ELEMENTS_LENGTH; +} + +inline uint32_t JSObject::ComputePropertyCapacity(uint32_t oldCapacity) +{ + uint32_t newCapacity = oldCapacity + PROPERTIES_GROW_SIZE; + return newCapacity > PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES ? PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES + : newCapacity; +} + +// static +template +JSHandle JSObject::CreateListFromArrayLike(JSThread *thread, const JSHandle &obj) +{ + // 3. If Type(obj) is not Object, throw a TypeError exception. + if (!obj->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "CreateListFromArrayLike must accept object", + JSHandle(thread, JSTaggedValue::Exception())); + } + // 4. Let len be ToLength(Get(obj, "length")). + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + + JSHandle value = GetProperty(thread, obj, lengthKeyHandle).GetValue(); + JSTaggedNumber number = JSTaggedValue::ToLength(thread, value); + // 5. ReturnIfAbrupt(len). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + if (number.GetNumber() > MAX_ELEMENT_INDEX) { + THROW_TYPE_ERROR_AND_RETURN(thread, "len is bigger than 2^32 - 1", + JSHandle(thread, JSTaggedValue::Exception())); + } + + uint32_t len = number.ToUint32(); + // 6. Let list be an empty List. + JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len); + + // 8. Repeat while index < len + for (uint32_t i = 0; i < len; i++) { + JSTaggedValue next = JSTaggedValue::GetProperty(thread, obj, i).GetValue().GetTaggedValue(); + // c. ReturnIfAbrupt(next). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + + if constexpr (types == ElementTypes::STRING_AND_SYMBOL) { + if (!next.IsString() && !next.IsSymbol()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "CreateListFromArrayLike: not an element of elementTypes", + JSHandle(thread, JSTaggedValue::Exception())); + } + } + + array->Set(thread, i, next); + } + return JSHandle(array); +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_JSOBJECT_INL_H diff --git a/ecmascript/js_object.cpp b/ecmascript/js_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2f724cf18e5a8ed8afa4861d4c34ca513b1cae4b --- /dev/null +++ b/ecmascript/js_object.cpp @@ -0,0 +1,1904 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "accessor_data.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_thread.h" +#include "global_dictionary-inl.h" +#include "js_array.h" +#include "js_for_in_iterator.h" +#include "js_hclass.h" +#include "js_invoker.h" +#include "js_iterator.h" +#include "object_factory.h" +#include "property_attributes.h" +#include "tagged_array-inl.h" + +namespace panda::ecmascript { +PropertyAttributes::PropertyAttributes(const PropertyDescriptor &desc) +{ + DISALLOW_GARBAGE_COLLECTION; + if (desc.HasWritable()) { + SetWritable(desc.IsWritable()); + } + + if (desc.HasEnumerable()) { + SetEnumerable(desc.IsEnumerable()); + } + + if (desc.HasConfigurable()) { + SetConfigurable(desc.IsConfigurable()); + } + + if (desc.IsAccessorDescriptor()) { + SetIsAccessor(true); + } + // internal accessor + if (desc.HasValue() && desc.GetValue()->IsAccessor()) { + SetIsAccessor(true); + } +} + +JSMethod *ECMAObject::GetCallTarget() const +{ + const TaggedObject *obj = this; + ASSERT(JSTaggedValue(obj).IsJSFunctionBase() || JSTaggedValue(obj).IsJSProxy()); + if (JSTaggedValue(obj).IsJSFunctionBase()) { + return JSFunctionBase::ConstCast(obj)->GetMethod(); + } + return JSProxy::ConstCast(obj)->GetMethod(); +} + +JSHandle JSObject::GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, + uint32_t capacity) +{ + capacity = ComputeElementCapacity(capacity); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle oldElements(thread, obj->GetElements()); + array_size_t oldLength = oldElements->GetLength(); + JSHandle newElements = factory->CopyArray(oldElements, oldLength, capacity); + + obj->SetElements(thread, newElements); + return newElements; +} + +bool JSObject::IsRegExp(JSThread *thread, const JSHandle &argument) +{ + if (!argument->IsECMAObject()) { + return false; + } + JSHandle matchSymbol = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchSymbol(); + JSHandle isRegexp = JSObject::GetProperty(thread, argument, matchSymbol).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!isRegexp->IsUndefined()) { + return isRegexp->ToBoolean(); + } + JSHandle argumentObj = JSHandle::Cast(argument); + return argumentObj->IsJSRegExp(); +} + +JSHandle JSObject::TransitionToDictionary(const JSThread *thread, const JSHandle &receiver) +{ + JSHandle array(thread, receiver->GetProperties()); + JSHClass *jshclass = receiver->GetJSHClass(); + ASSERT(!jshclass->IsDictionaryMode()); + int propNumber = jshclass->GetPropertiesNumber(); + + ASSERT(propNumber >= 0); + ASSERT(!jshclass->GetAttributes().IsNull()); + JSHandle layoutInfoHandle(thread, jshclass->GetAttributes()); + ASSERT(layoutInfoHandle->GetLength() != 0); + JSMutableHandle dict( + thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(propNumber))); + JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + uint32_t numberInlinedProps = 0; + for (int i = 0; i < propNumber; i++) { + JSTaggedValue key = layoutInfoHandle->GetKey(i); + PropertyAttributes attr = layoutInfoHandle->GetAttr(i); + ASSERT(i == static_cast(attr.GetOffset())); + JSTaggedValue value; + + if (attr.IsInlinedProps()) { + value = receiver->GetPropertyInlinedProps(i); + receiver->SetPropertyInlinedProps(thread, i, JSTaggedValue::Undefined()); + numberInlinedProps++; + } else { + value = array->Get(i - numberInlinedProps); + } + + attr.SetBoxType(PropertyBoxType::UNDEFINED); + valueHandle.Update(value); + keyHandle.Update(key); + dict.Update(JSTaggedValue(NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr))); + } + + receiver->SetProperties(thread, dict); + // change HClass + JSHClass::TransitionToDictionary(thread, receiver); + return dict; +} + +void JSObject::ElementsToDictionary(const JSThread *thread, JSHandle obj) +{ + JSHandle elements(thread, obj->GetElements()); + ASSERT(!obj->GetJSHClass()->IsDictionaryElement()); + int length = elements->GetLength(); + JSMutableHandle dict(thread, NumberDictionary::Create(thread)); + auto attr = PropertyAttributes(PropertyAttributes::GetDefaultAttributes()); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle valueHandle(thread, JSTaggedValue ::Undefined()); + for (int i = 0; i < length; i++) { + JSTaggedValue value = elements->Get(i); + if (value.IsHole()) { + continue; + } + key.Update(JSTaggedValue(i)); + valueHandle.Update(value); + dict.Update(JSTaggedValue(NumberDictionary::PutIfAbsent(thread, dict, key, valueHandle, attr))); + } + obj->SetElements(thread, dict); + + JSHClass::TransitionElementsToDictionary(thread, obj); +} + +bool JSObject::IsArrayLengthWritable(JSThread *thread, const JSHandle &receiver) +{ + auto *hclass = receiver->GetJSHClass(); + if (!hclass->IsDictionaryMode()) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetAttributes().GetTaggedObject()); + PropertyAttributes attr(layoutInfo->GetAttr(JSArray::LENGTH_INLINE_PROPERTY_INDEX)); + return attr.IsWritable(); + } + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + ObjectOperator op(thread, receiver, lengthKey, OperatorType::OWN); + return op.GetAttr().IsWritable(); +} + +bool JSObject::AddElementInternal(JSThread *thread, const JSHandle &receiver, uint32_t index, + const JSHandle &value, PropertyAttributes attr) +{ + bool isDictionary = receiver->GetJSHClass()->IsDictionaryElement(); + if (receiver->IsJSArray()) { + DISALLOW_GARBAGE_COLLECTION; + JSArray *arr = JSArray::Cast(*receiver); + uint32_t oldLength = arr->GetArrayLength(); + if (index >= oldLength) { + if (!IsArrayLengthWritable(thread, receiver)) { + return false; + } + arr->SetArrayLength(thread, index + 1); + } + } + thread->NotifyStableArrayElementsGuardians(receiver); + + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + if (isDictionary) { + ASSERT(elements->IsDictionaryMode()); + JSHandle keyHandle(thread, JSTaggedValue(static_cast(index))); + NumberDictionary *dict = + NumberDictionary::Put(thread, JSHandle(thread, elements), keyHandle, value, attr); + receiver->SetElements(thread, JSTaggedValue(dict)); + return true; + } + + uint32_t capacity = elements->GetLength(); + if (index >= capacity || !attr.IsDefaultAttributes()) { + if (ShouldTransToDict(capacity, index) || !attr.IsDefaultAttributes()) { + JSObject::ElementsToDictionary(thread, receiver); + JSHandle keyHandle(thread, JSTaggedValue(static_cast(index))); + JSHandle dict(thread, receiver->GetElements()); + auto key = JSTaggedValue(NumberDictionary::Put(thread, dict, keyHandle, value, attr)); + receiver->SetElements(thread, key); + return true; + } + elements = *JSObject::GrowElementsCapacity(thread, receiver, index + 1); + } + elements->Set(thread, index, value); + receiver->GetJSHClass()->UpdateRepresentation(value.GetTaggedValue()); + return true; +} + +void JSObject::DeletePropertyInternal(JSThread *thread, const JSHandle &obj, + const JSHandle &key, uint32_t index) +{ + JSMutableHandle array(thread, obj->GetProperties()); + + if (obj->IsJSGlobalObject()) { + JSHandle dictHandle(thread, obj->GetProperties()); + GlobalDictionary *dict = GlobalDictionary::Remove(thread, dictHandle, index); + obj->SetProperties(thread, JSTaggedValue(dict)); + return; + } + + if (!array->IsDictionaryMode()) { + JSHandle dictHandle(TransitionToDictionary(thread, obj)); + int entry = dictHandle->FindEntry(key.GetTaggedValue()); + ASSERT(entry != -1); + NameDictionary *dict = NameDictionary::Remove(thread, dictHandle, entry); + obj->SetProperties(thread, JSTaggedValue(dict)); + return; + } + + JSHandle dictHandle(array); + NameDictionary *dict = NameDictionary::Remove(thread, dictHandle, index); + obj->SetProperties(thread, JSTaggedValue(dict)); +} + +void JSObject::GetAllKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray) + +{ + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + int end = obj->GetJSHClass()->GetPropertiesNumber(); + if (end > 0) { + LayoutInfo::Cast(obj->GetJSHClass()->GetAttributes().GetTaggedObject()) + ->GetAllKeys(thread, end, offset, *keyArray); + } + return; + } + + if (obj->IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(array); + return dict->GetAllKeys(thread, offset, *keyArray); + } + + NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetAllKeys(thread, offset, *keyArray); +} + +// For Serialization use. Does not support JSGlobalObject +void JSObject::GetAllKeys(const JSThread *thread, const JSHandle &obj, + std::vector &keyVector) +{ + DISALLOW_GARBAGE_COLLECTION; + ASSERT_PRINT(!obj->IsJSGlobalObject(), "Do not support get key of JSGlobal Object"); + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + int end = obj->GetJSHClass()->GetPropertiesNumber(); + if (end > 0) { + LayoutInfo::Cast(obj->GetJSHClass()->GetAttributes().GetTaggedObject()) + ->GetAllKeys(thread, end, keyVector); + } + } else { + NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetAllKeysIntoVector(thread, keyVector); + } +} + +JSHandle JSObject::GetAllEnumKeys(const JSThread *thread, const JSHandle &obj, int offset, + uint32_t numOfKeys, array_size_t *keys) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (obj->IsJSGlobalObject()) { + JSHandle keyArray = factory->NewTaggedArray(numOfKeys); + GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetEnumAllKeys(thread, offset, *keyArray, keys); + return keyArray; + } + + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + JSHClass *jsHclass = obj->GetJSHClass(); + JSTaggedValue enumCache = jsHclass->GetEnumCache(); + if (!enumCache.IsNull()) { + auto keyArray = JSHandle(thread, enumCache); + *keys = keyArray->GetLength(); + return keyArray; + } + JSHandle keyArray = factory->NewTaggedArray(numOfKeys); + int end = jsHclass->GetPropertiesNumber(); + if (end > 0) { + LayoutInfo::Cast(jsHclass->GetAttributes().GetTaggedObject()) + ->GetAllEnumKeys(thread, end, offset, *keyArray, keys); + if (*keys == keyArray->GetLength()) { + jsHclass->SetEnumCache(thread, keyArray.GetTaggedValue()); + } + } + return keyArray; + } + + JSHandle keyArray = factory->NewTaggedArray(numOfKeys); + NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetAllEnumKeys(thread, offset, *keyArray, keys); + return keyArray; +} + +void JSObject::GetAllElementKeys(JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray) +{ + uint32_t elementIndex = 0; + + if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { + elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength() + offset; + for (uint32_t i = offset; i < elementIndex; ++i) { + auto key = base::NumberHelper::NumberToString(thread, JSTaggedValue(i)); + keyArray->Set(thread, i, key); + } + } + + JSHandle elements(thread, obj->GetElements()); + if (!elements->IsDictionaryMode()) { + uint32_t elementsLen = elements->GetLength(); + for (uint32_t i = 0, j = elementIndex; i < elementsLen; ++i) { + if (!elements->Get(i).IsHole()) { + auto key = base::NumberHelper::NumberToString(thread, JSTaggedValue(i)); + keyArray->Set(thread, j++, key); + } + } + } else { + NumberDictionary::GetAllKeys(thread, JSHandle(elements), elementIndex, keyArray); + } +} + +void JSObject::GetALLElementKeysIntoVector(const JSThread *thread, const JSHandle &obj, + std::vector &keyVector) +{ + JSHandle elements(thread, obj->GetElements()); + if (!elements->IsDictionaryMode()) { + uint32_t elementsLen = elements->GetLength(); + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!elements->Get(i).IsHole()) { + keyVector.emplace_back(JSTaggedValue(i)); + } + } + } else { + JSHandle dict = JSHandle::Cast(elements); + dict->GetAllKeysIntoVector(thread, keyVector); + } +} + +JSHandle JSObject::GetEnumElementKeys(JSThread *thread, const JSHandle &obj, int offset, + uint32_t numOfElements, array_size_t *keys) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle elementArray = factory->NewTaggedArray(numOfElements); + uint32_t elementIndex = 0; + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + + if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { + elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength(); + *keys += elementIndex; + elementIndex += offset; + for (uint32_t i = offset; i < elementIndex; ++i) { + keyHandle.Update(JSTaggedValue(i)); + auto key = JSTaggedValue::ToString(thread, keyHandle); + elementArray->Set(thread, i, key); + } + } + + JSHandle arr(thread, obj->GetElements()); + if (!arr->IsDictionaryMode()) { + uint32_t elementsLen = arr->GetLength(); + uint32_t preElementIndex = elementIndex; + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!arr->Get(i).IsHole()) { + keyHandle.Update(JSTaggedValue(i)); + auto key = JSTaggedValue::ToString(thread, keyHandle); + elementArray->Set(thread, elementIndex++, key); + } + } + *keys += (elementIndex - preElementIndex); + } else { + NumberDictionary::GetAllEnumKeys(thread, JSHandle(arr), elementIndex, elementArray, keys); + } + return elementArray; +} + +uint32_t JSObject::GetNumberOfKeys() +{ + DISALLOW_GARBAGE_COLLECTION; + TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); + + if (!array->IsDictionaryMode()) { + return GetJSHClass()->GetPropertiesNumber(); + } + + return NameDictionary::Cast(array)->EntriesCount(); +} + +bool JSObject::GlobalSetProperty(JSThread *thread, const JSHandle &key, + const JSHandle &value, bool mayThrow) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, key); + if (!op.IsFound()) { + PropertyAttributes attr = PropertyAttributes::Default(true, true, false); + op.SetAttr(attr); + } + return SetProperty(&op, value, mayThrow); +} +uint32_t JSObject::GetNumberOfElements() +{ + DISALLOW_GARBAGE_COLLECTION; + uint32_t numOfElements = 0; + if (IsJSPrimitiveRef() && JSPrimitiveRef::Cast(this)->IsString()) { + numOfElements = JSPrimitiveRef::Cast(this)->GetStringLength(); + } + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + if (!elements->IsDictionaryMode()) { + uint32_t elementsLen = elements->GetLength(); + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!elements->Get(i).IsHole()) { + numOfElements++; + } + } + } else { + numOfElements += NumberDictionary::Cast(elements)->EntriesCount(); + } + + return numOfElements; +} + +// 9.1.9 [[Set]] ( P, V, Receiver) +bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver, bool mayThrow) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + // 2 ~ 4 findProperty in Receiver, Obj and its parents + ObjectOperator op(thread, obj, receiver, key); + return SetProperty(&op, value, mayThrow); +} + +bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, bool mayThrow) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid JSObject"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, obj, key); + return SetProperty(&op, value, mayThrow); +} + +bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, bool mayThrow) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + // 2 ~ 4 findProperty in Receiver, Obj and its parents + ObjectOperator op(thread, obj, key); + return SetProperty(&op, value, mayThrow); +} + +bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value, bool mayThrow) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + + ObjectOperator op(thread, obj, index); + return SetProperty(&op, value, mayThrow); +} + +bool JSObject::SetProperty(ObjectOperator *op, const JSHandle &value, bool mayThrow) +{ + JSThread *thread = op->GetThread(); + + JSHandle receiver = op->GetReceiver(); + JSHandle holder = op->GetHolder(); + if (holder->IsJSProxy()) { + if (op->IsElement()) { + JSHandle key(thread, JSTaggedValue(op->GetElementIndex())); + return JSProxy::SetProperty(thread, JSHandle::Cast(holder), key, value, receiver, mayThrow); + } + return JSProxy::SetProperty(thread, JSHandle::Cast(holder), op->GetKey(), value, receiver, mayThrow); + } + + // When op is not found and is not set extra attributes + if (!op->IsFound() && op->IsPrimitiveAttr()) { + op->SetAsDefaultAttr(); + } + + bool isInternalAccessor = false; + if (op->IsAccessorDescriptor()) { + isInternalAccessor = AccessorData::Cast(op->GetValue().GetTaggedObject())->IsInternal(); + } + + // 5. If IsDataDescriptor(ownDesc) is true, then + if (!op->IsAccessorDescriptor() || isInternalAccessor) { + if (!op->IsWritable()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property", false); + } + return false; + } + + if (!receiver->IsECMAObject()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Receiver is not a JSObject", false); + } + return false; + } + + if (receiver->IsJSProxy()) { + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + if (op->IsElement()) { + key.Update(JSTaggedValue(op->GetElementIndex())); + } else { + key.Update(op->GetKey().GetTaggedValue()); + } + + PropertyDescriptor existDesc(thread); + JSProxy::GetOwnProperty(thread, JSHandle::Cast(receiver), key, existDesc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!existDesc.IsEmpty()) { + if (existDesc.IsAccessorDescriptor()) { + return false; + } + + if (!existDesc.IsWritable()) { + return false; + } + + PropertyDescriptor valueDesc(thread, value); + return JSProxy::DefineOwnProperty(thread, JSHandle::Cast(receiver), key, valueDesc); + } + return CreateDataProperty(thread, JSHandle(receiver), key, value); + } + + // 5e. If existingDescriptor is not undefined, then + bool hasReceiver = false; + if (op->HasReceiver()) { + op->ReLookupPropertyInReceiver(); + hasReceiver = true; + } + bool isSuccess = true; + if (op->IsFound() && !op->IsOnPrototype()) { + // i. If IsAccessorDescriptor(existingDescriptor) is true, return false. + if (op->IsAccessorDescriptor() && !isInternalAccessor) { + return false; + } + + // ii. If existingDescriptor.[[Writable]] is false, return false. + if (!op->IsWritable()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property", false); + } + return false; + } + isSuccess = op->UpdateDataValue(JSHandle(receiver), value, isInternalAccessor, mayThrow); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, isSuccess); + } else { + // 5f. Else if Receiver does not currently have a property P, Return CreateDataProperty(Receiver, P, V). + if (!receiver->IsExtensible(thread)) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "receiver is not Extensible", false); + } + return false; + } + if (LIKELY(!hasReceiver)) { + return op->AddProperty(JSHandle(receiver), value, op->GetAttr()); + } else { + PropertyAttributes attr; + attr.SetDefaultAttributes(); + return op->AddProperty(JSHandle(receiver), value, attr); + } + } + return isSuccess; + } + // 6. Assert: IsAccessorDescriptor(ownDesc) is true. + ASSERT(op->IsAccessorDescriptor()); + // 8. If setter is undefined, return false. + AccessorData *accessor = AccessorData::Cast(op->GetValue().GetTaggedObject()); + return CallSetter(thread, *accessor, receiver, value, mayThrow); +} + +bool JSObject::CallSetter(JSThread *thread, const AccessorData &accessor, const JSHandle &receiver, + const JSHandle &value, bool mayThrow) +{ + if (UNLIKELY(accessor.IsInternal())) { + return accessor.CallInternalSet(thread, JSHandle::Cast(receiver), value, mayThrow); + } + JSTaggedValue setter = accessor.GetSetter(); + // 8. If setter is undefined, return false. + if (setter.IsUndefined()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property when setter is undefined", false); + } + return false; + } + + JSHandle func(thread, setter); + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1); + args->Set(thread, 0, value); + JSFunction::Call(thread, func, receiver, args); + // 10. ReturnIfAbrupt(setterResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + return true; +} + +JSTaggedValue JSObject::CallGetter(JSThread *thread, const AccessorData *accessor, + const JSHandle &receiver) +{ + JSTaggedValue getter = accessor->GetGetter(); + // 7. If getter is undefined, return undefined. + if (getter.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + + JSHandle func(thread, getter); + JSHandle args = thread->GetEcmaVM()->GetFactory()->EmptyArray(); + JSTaggedValue res = JSFunction::Call(thread, func, receiver, args); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res; +} + +// 9.1.8 [[Get]] (P, Receiver) +OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &receiver) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, obj, receiver, key); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); +} + +OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid JSObject"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, obj, key); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); +} + +OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, obj, key); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); +} + +OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, uint32_t index) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + + ObjectOperator op(thread, obj, index); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); +} + +OperationResult JSObject::GetPropertyFromGlobal(JSThread *thread, const JSHandle &key) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, key); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); +} + +JSTaggedValue JSObject::GetProperty(JSThread *thread, ObjectOperator *op) +{ + JSHandle receiver = op->GetReceiver(); + JSHandle holder = op->GetHolder(); + if (holder->IsJSProxy()) { + if (op->IsElement()) { + JSHandle key(thread, JSTaggedValue(op->GetElementIndex())); + return JSProxy::GetProperty(thread, JSHandle::Cast(holder), op->GetKey(), receiver) + .GetValue() + .GetTaggedValue(); + } + return JSProxy::GetProperty(thread, JSHandle::Cast(holder), op->GetKey(), receiver) + .GetValue() + .GetTaggedValue(); + } + + // 4. If desc is undefined, then + if (!op->IsFound()) { + // 4c. If obj and parent is null, return undefined. + return JSTaggedValue::Undefined(); + } + // 5. If IsDataDescriptor(desc) is true, return desc.[[Value]] + if (!op->IsAccessorDescriptor()) { + JSTaggedValue ret = op->GetValue(); + if (ret.IsPropertyBox()) { + ret = PropertyBox::Cast(ret.GetTaggedObject())->GetValue(); + } + return ret; + } + // 6. Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be desc.[[Get]]. + + AccessorData *accessor = AccessorData::Cast(op->GetValue().GetTaggedObject()); + // 8. Return Call(getter, Receiver). + if (UNLIKELY(accessor->IsInternal())) { + return accessor->CallInternalGet(thread, JSHandle::Cast(holder)); + } + return CallGetter(thread, accessor, receiver); +} + +bool JSObject::DeleteProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key) +{ + // 1. Assert: IsPropertyKey(P) is true. + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + // 2. Let desc be O.[[GetOwnProperty]](P). + + ObjectOperator op(thread, JSHandle(obj), key, OperatorType::OWN); + + // 4. If desc is undefined, return true. + if (!op.IsFound()) { + return true; + } + // 5. If desc.[[Configurable]] is true, then + // a. Remove the own property with name P from O. + // b. Return true. + // 6. Return false. + if (op.IsConfigurable()) { + op.DeletePropertyInHolder(); + return true; + } + return false; +} + +bool JSObject::GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + PropertyDescriptor &desc) +{ + return OrdinaryGetOwnProperty(thread, obj, key, desc); +} + +bool JSObject::GlobalGetOwnProperty(JSThread *thread, const JSHandle &key, PropertyDescriptor &desc) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ObjectOperator op(thread, key, OperatorType::OWN); + + if (!op.IsFound()) { + return false; + } + + op.ToPropertyDescriptor(desc); + + if (desc.HasValue()) { + PropertyBox *cell = PropertyBox::Cast(desc.GetValue().GetTaggedValue().GetTaggedObject()); + JSHandle valueHandle(thread, cell->GetValue()); + desc.SetValue(valueHandle); + } + ASSERT(!desc.GetValue()->IsInternalAccessor()); + return true; +} + +bool JSObject::OrdinaryGetOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ObjectOperator op(thread, JSHandle(obj), key, OperatorType::OWN); + + if (!op.IsFound()) { + return false; + } + + op.ToPropertyDescriptor(desc); + + if (desc.HasValue() && obj->IsJSGlobalObject()) { + PropertyBox *cell = PropertyBox::Cast(desc.GetValue().GetTaggedValue().GetTaggedObject()); + JSHandle valueHandle(thread, cell->GetValue()); + desc.SetValue(valueHandle); + } + + return true; +} + +bool JSObject::DefineOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const PropertyDescriptor &desc) +{ + return OrdinaryDefineOwnProperty(thread, obj, key, desc); +} + +bool JSObject::DefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const PropertyDescriptor &desc) +{ + return OrdinaryDefineOwnProperty(thread, obj, index, desc); +} + +// 9.1.6.1 OrdinaryDefineOwnProperty (O, P, Desc) +bool JSObject::OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + // 1. Let current be O.[[GetOwnProperty]](P). + JSHandle objValue(obj); + ObjectOperator op(thread, objValue, key, OperatorType::OWN); + + bool extensible = obj->IsExtensible(); + PropertyDescriptor current(thread); + op.ToPropertyDescriptor(current); + // 4. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current). + return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current); +} + +bool JSObject::OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const PropertyDescriptor &desc) +{ + JSHandle objValue(obj); + ObjectOperator op(thread, objValue, index, OperatorType::OWN); + + bool extensible = obj->IsExtensible(); + PropertyDescriptor current(thread); + op.ToPropertyDescriptor(current); + return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current); +} + +// 9.1.6.3 ValidateAndApplyPropertyDescriptor (O, P, extensible, Desc, current) +bool JSObject::ValidateAndApplyPropertyDescriptor(ObjectOperator *op, bool extensible, const PropertyDescriptor &desc, + const PropertyDescriptor ¤t) +{ + // 2. If current is undefined, then + if (current.IsEmpty()) { + // 2a. If extensible is false, return false. + if (!extensible) { + return false; + } + if (!op->HasHolder()) { + return true; + } + + // 2c. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then + PropertyAttributes attr(desc); + bool success = false; + if (!desc.IsAccessorDescriptor()) { + success = op->AddPropertyInHolder(desc.GetValue(), attr); + } else { // is AccessorDescriptor + // may GC in NewAccessorData, so we need to handle getter and setter. + JSThread *thread = op->GetThread(); + JSHandle accessor = thread->GetEcmaVM()->GetFactory()->NewAccessorData(); + if (desc.HasGetter()) { + accessor->SetGetter(thread, desc.GetGetter()); + } + + if (desc.HasSetter()) { + accessor->SetSetter(thread, desc.GetSetter()); + } + success = op->AddPropertyInHolder(JSHandle::Cast(accessor), attr); + } + + return success; + } + + // 3. Return true, if every field in Desc is absent + // 4. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the + // same value as the corresponding field in current when compared using the SameValue algorithm. + if ((!desc.HasEnumerable() || desc.IsEnumerable() == current.IsEnumerable()) && + (!desc.HasConfigurable() || desc.IsConfigurable() == current.IsConfigurable()) && + (!desc.HasValue() || JSTaggedValue::SameValue(current.GetValue(), desc.GetValue())) && + (!desc.HasWritable() || (current.IsWritable() == desc.IsWritable())) && + (!desc.HasGetter() || + (current.HasGetter() && JSTaggedValue::SameValue(current.GetGetter(), desc.GetGetter()))) && + (!desc.HasSetter() || + (current.HasSetter() && JSTaggedValue::SameValue(current.GetSetter(), desc.GetSetter())))) { + return true; + } + + // 5. If the [[Configurable]] field of current is false, then + if (!current.IsConfigurable()) { + // 5a. Return false, if the [[Configurable]] field of Desc is true. + if (desc.HasConfigurable() && desc.IsConfigurable()) { + return false; + } + // b. Return false, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current + // and Desc are the Boolean negation of each other. + if (desc.HasEnumerable() && (desc.IsEnumerable() != current.IsEnumerable())) { + return false; + } + } + + // 6. If IsGenericDescriptor(Desc) is true, no further validation is required. + if (desc.IsGenericDescriptor()) { + // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then + } else if (current.IsDataDescriptor() != desc.IsDataDescriptor()) { + // 7a. Return false, if the [[Configurable]] field of current is false. + if (!current.IsConfigurable()) { + return false; + } + // 7b. If IsDataDescriptor(current) is true, then + if (current.IsDataDescriptor()) { + // 7bi. If O is not undefined, convert the property named P of object O from a data property to an + // accessor property. Preserve the existing values of the converted property’s [[Configurable]] and + // [[Enumerable]] attributes and set the rest of the property’s attributes to their default values. + } else { + // 7ci. If O is not undefined, convert the property named P of object O from an accessor property to a + // data property. Preserve the existing values of the converted property’s [[Configurable]] and + // [[Enumerable]] attributes and set the rest of the property’s attributes to their default values. + } + // 8. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then + } else if (current.IsDataDescriptor() && desc.IsDataDescriptor()) { + // 8a. If the [[Configurable]] field of current is false, then + if (!current.IsConfigurable()) { + // 8a i. Return false, if the [[Writable]] field of current is false and the [[Writable]] field of Desc + // is true. + if (!current.IsWritable() && desc.HasWritable() && desc.IsWritable()) { + return false; + } + // 8a ii. If the [[Writable]] field of current is false, then + if (!current.IsWritable()) { + if (desc.HasValue() && !JSTaggedValue::SameValue(current.GetValue(), desc.GetValue())) { + return false; + } + } + } + // 8b. Else the [[Configurable]] field of current is true, so any change is acceptable. + } else { // 9. Else IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true, + // 9a. If the [[Configurable]] field of current is false, then + if (!current.IsConfigurable()) { + // i. Return false, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) + // is false. + if (desc.HasSetter() && !JSTaggedValue::SameValue(current.GetSetter(), desc.GetSetter())) { + return false; + } + // ii. Return false, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], + // current.[[Get]]) is false. + if (desc.HasGetter() && !JSTaggedValue::SameValue(current.GetGetter(), desc.GetGetter())) { + return false; + } + } + } + + if (op->HasHolder()) { + // 10. If O is not undefined, then + // a. For each field of Desc that is present, set the corresponding attribute of the property named P of object + // O to the value of the field. + return op->WriteDataPropertyInHolder(desc); + } + return true; +} + +// 9.1.6.2 IsCompatiblePropertyDescriptor (Extensible, Desc, Current) +bool JSObject::IsCompatiblePropertyDescriptor(bool extensible, const PropertyDescriptor &desc, + const PropertyDescriptor ¤t) +{ + // 1. Return ValidateAndApplyPropertyDescriptor(undefined, undefined, Extensible, Desc, Current). + ObjectOperator op; + return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current); +} + +JSTaggedValue JSObject::GetPrototype(JSThread *thread) const +{ + JSHandle obj(thread, JSTaggedValue(this)); + if (obj->IsJSProxy()) { + return JSProxy::GetPrototype(thread, JSHandle(obj)); + } + JSHClass *hclass = GetJSHClass(); + return hclass->GetPrototype(); +} + +bool JSObject::SetPrototype(JSThread *thread, const JSHandle &obj, const JSHandle &proto) +{ + ASSERT_PRINT(proto->IsECMAObject() || proto->IsNull(), "proto must be object or null"); + JSTaggedValue current = obj->GetPrototype(thread); + if (current == proto.GetTaggedValue()) { + return true; + } + if (!obj->IsExtensible()) { + return false; + } + bool done = false; + JSTaggedValue tempProto = proto.GetTaggedValue(); + while (!done) { + if (tempProto.IsNull() || !tempProto.IsECMAObject()) { + done = true; + } else if (JSTaggedValue::SameValue(tempProto, obj.GetTaggedValue())) { + return false; + } else { + if (tempProto.IsJSProxy()) { + break; + } + tempProto = JSObject::Cast(tempProto.GetTaggedObject())->GetPrototype(thread); + } + } + // map transition + JSHandle dynclass(thread, obj->GetJSHClass()); + JSHandle newDynclass = JSHClass::TransitionProto(thread, dynclass, proto); + JSHClass::NotifyHclassChanged(thread, dynclass, newDynclass); + obj->SetClass(newDynclass); + thread->NotifyStableArrayElementsGuardians(obj); + return true; +} + +bool JSObject::HasProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + JSHandle objValue(obj); + ObjectOperator op(thread, objValue, key); + + JSHandle holder = op.GetHolder(); + if (holder->IsJSProxy()) { + return JSProxy::HasProperty(thread, JSHandle::Cast(holder), key); + } + + return op.IsFound(); +} + +bool JSObject::HasProperty(JSThread *thread, const JSHandle &obj, uint32_t index) +{ + JSHandle objValue(obj); + ObjectOperator op(thread, objValue, index); + + JSHandle holder = op.GetHolder(); + if (holder->IsJSProxy()) { + JSHandle key(thread, JSTaggedValue(index)); + return JSProxy::HasProperty(thread, JSHandle::Cast(holder), key); + } + + return op.IsFound(); +} + +bool JSObject::PreventExtensions(JSThread *thread, const JSHandle &obj) +{ + if (obj->IsExtensible()) { + JSHandle jshclass(thread, obj->GetJSHClass()); + JSHandle newHclass = JSHClass::TransitionExtension(thread, jshclass); + obj->SetClass(newHclass); + } + + return true; +} + +// 9.1.12 [[OwnPropertyKeys]] ( ) +JSHandle JSObject::GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj) +{ + [[maybe_unused]] uint32_t elementIndex = 0; + uint32_t numOfElements = obj->GetNumberOfElements(); + uint32_t keyLen = numOfElements + obj->GetNumberOfKeys(); + + JSHandle keyArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(keyLen); + + if (numOfElements > 0) { + GetAllElementKeys(thread, obj, 0, keyArray); + } + GetAllKeys(thread, obj, static_cast(numOfElements), keyArray); + return keyArray; +} + +JSHandle JSObject::ObjectCreate(JSThread *thread, const JSHandle &proto) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle constructor = env->GetObjectFunction(); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + SetPrototype(thread, objHandle, JSHandle(proto)); + return objHandle; +} + +// 7.3.4 CreateDataProperty (O, P, V) +bool JSObject::CreateDataProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + auto result = FastRuntimeStub::SetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(), + value.GetTaggedValue()); + if (!result.IsHole()) { + return result != JSTaggedValue::Exception(); + } + PropertyDescriptor desc(thread, value, true, true, true); + return JSTaggedValue::DefineOwnProperty(thread, JSHandle::Cast(obj), key, desc); +} + +bool JSObject::CreateDataProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + auto result = + FastRuntimeStub::SetPropertyByIndex(thread, obj.GetTaggedValue(), index, value.GetTaggedValue()); + if (!result.IsHole()) { + return result != JSTaggedValue::Exception(); + } + PropertyDescriptor desc(thread, value, true, true, true); + return DefineOwnProperty(thread, obj, index, desc); +} + +// 7.3.5 CreateMethodProperty (O, P, V) +bool JSObject::CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + bool success = CreateDataProperty(thread, obj, key, value); + if (!success) { + THROW_TYPE_ERROR_AND_RETURN(thread, "failed to create data property", success); + } + return success; +} + +bool JSObject::CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + + bool success = CreateDataProperty(thread, obj, index, value); + if (!success) { + THROW_TYPE_ERROR_AND_RETURN(thread, "failed to create data property", success); + } + return success; +} +// 7.3.6 CreateDataPropertyOrThrow (O, P, V) +bool JSObject::CreateMethodProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + PropertyDescriptor desc(thread, value, true, false, true); + return DefineOwnProperty(thread, obj, key, desc); +} + +// 7.3.9 GetMethod (O, P) +JSHandle JSObject::GetMethod(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + JSHandle func = JSTaggedValue::GetProperty(thread, obj, key).GetValue(); + if (func->IsUndefined() || func->IsNull()) { + return JSHandle(thread, JSTaggedValue::Undefined()); + } + + if (!func->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not Callable", func); + } + return func; +} + +// 7.3.14 SetIntegrityLevel (O, level) +bool JSObject::SetIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT((level == IntegrityLevel::SEALED || level == IntegrityLevel::FROZEN), + "level is not a valid IntegrityLevel"); + + bool status = PreventExtensions(thread, obj); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!status) { + return false; + } + + JSHandle jshandleKeys = GetOwnPropertyKeys(thread, obj); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + PropertyDescriptor descNoConf(thread); + descNoConf.SetConfigurable(false); + PropertyDescriptor descNoConfWrite(thread); + descNoConfWrite.SetWritable(false); + descNoConfWrite.SetConfigurable(false); + + if (level == IntegrityLevel::SEALED) { + array_size_t length = jshandleKeys->GetLength(); + if (length == 0) { + return true; + } + auto key = jshandleKeys->Get(0); + JSMutableHandle handleKey(thread, key); + for (array_size_t i = 0; i < length; i++) { + auto taggedKey = JSTaggedValue(jshandleKeys->Get(i)); + handleKey.Update(taggedKey); + [[maybe_unused]] bool success = + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), handleKey, descNoConf); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + } + } else { + array_size_t length = jshandleKeys->GetLength(); + if (length == 0) { + return true; + } + auto key = jshandleKeys->Get(0); + JSMutableHandle handleKey(thread, key); + for (array_size_t i = 0; i < length; i++) { + auto taggedKey = JSTaggedValue(jshandleKeys->Get(i)); + handleKey.Update(taggedKey); + PropertyDescriptor currentDesc(thread); + bool curDescStatus = GetOwnProperty(thread, obj, handleKey, currentDesc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (curDescStatus) { + PropertyDescriptor desc = currentDesc.IsAccessorDescriptor() ? descNoConf : descNoConfWrite; + [[maybe_unused]] bool success = + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), handleKey, desc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + } + } + } + return true; +} + +// 7.3.15 TestIntegrityLevel (O, level) +bool JSObject::TestIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT((level == IntegrityLevel::SEALED || level == IntegrityLevel::FROZEN), + "level is not a valid IntegrityLevel"); + + bool status = obj->IsExtensible(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (status) { + return false; + } + + JSHandle jshandleKeys = GetOwnPropertyKeys(thread, obj); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + array_size_t length = jshandleKeys->GetLength(); + if (length == 0) { + return true; + } + auto key = jshandleKeys->Get(0); + JSMutableHandle handleKey(thread, key); + for (array_size_t i = 0; i < length; i++) { + auto taggedKey = JSTaggedValue(jshandleKeys->Get(i)); + handleKey.Update(taggedKey); + PropertyDescriptor currentDesc(thread); + bool curDescStatus = GetOwnProperty(thread, obj, handleKey, currentDesc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (curDescStatus) { + if (currentDesc.IsConfigurable()) { + return false; + } + if (level == IntegrityLevel::FROZEN && currentDesc.IsDataDescriptor() && currentDesc.IsWritable()) { + return false; + } + } + } + return true; +} + +// 7.3.21 EnumerableOwnNames (O) +JSHandle JSObject::EnumerableOwnNames(JSThread *thread, const JSHandle &obj) +{ + ASSERT_PRINT(obj->IsECMAObject(), "obj is not object"); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle keys; + JSHandle tagObj(obj); + array_size_t copyLength = 0; + // fast mode + if (tagObj->IsJSObject() && !tagObj->IsTypedArray()) { + uint32_t numOfKeys = obj->GetNumberOfKeys(); + uint32_t numOfElements = obj->GetNumberOfElements(); + JSHandle keyArray; + JSHandle elementArray; + if (numOfElements > 0) { + elementArray = JSObject::GetEnumElementKeys(thread, obj, 0, numOfElements, ©Length); + } + + if (numOfKeys > 0) { + keyArray = JSObject::GetAllEnumKeys(thread, obj, 0, numOfKeys, ©Length); + } + + if (numOfKeys != 0 && numOfElements != 0) { + keys = TaggedArray::AppendSkipHole(thread, elementArray, keyArray, copyLength); + } else if (numOfKeys != 0) { + keys = factory->CopyArray(keyArray, copyLength, copyLength); + } else if (numOfElements != 0) { + keys = factory->CopyArray(elementArray, copyLength, copyLength); + } else { + keys = factory->EmptyArray(); + } + return keys; + } + + keys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle(obj)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + array_size_t length = keys->GetLength(); + + JSHandle names = factory->NewTaggedArray(length); + for (array_size_t i = 0; i < length; i++) { + JSTaggedValue key(keys->Get(i)); + if (key.IsString()) { + PropertyDescriptor desc(thread); + bool status = GetOwnProperty(thread, obj, JSHandle(thread, key), desc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + if (status && desc.IsEnumerable()) { + names->Set(thread, copyLength, key); + copyLength++; + } + } + } + + JSHandle resultList = factory->CopyArray(names, length, copyLength); + return resultList; +} + +JSHandle JSObject::EnumerableOwnPropertyNames(JSThread *thread, const JSHandle &obj, + PropertyKind kind) +{ + // 1. Assert: Type(O) is Object. + ASSERT_PRINT(obj->IsECMAObject(), "obj is not object"); + + // 2. Let ownKeys be ? O.[[OwnPropertyKeys]](). + JSHandle ownKeys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle(obj)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + // 3. Let properties be a new empty List. + array_size_t length = ownKeys->GetLength(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle properties = factory->NewTaggedArray(length); + + // 4. For each element key of ownKeys, do + // a. If Type(key) is String, then + // i. Let desc be ? O.[[GetOwnProperty]](key). + // ii. If desc is not undefined and desc.[[Enumerable]] is true, then + // 1. If kind is key, append key to properties. + // 2. Else, + // a. Let value be ? Get(O, key). + // b. If kind is value, append value to properties. + // c. Else, + // i. Assert: kind is key+value. + // ii. Let entry be ! CreateArrayFromList(« key, value »). + // iii. Append entry to properties. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + array_size_t index = 0; + for (array_size_t i = 0; i < length; i++) { + key.Update(ownKeys->Get(thread, i)); + if (key->IsString()) { + PropertyDescriptor desc(thread); + bool status = GetOwnProperty(thread, obj, JSHandle(key), desc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + if (status && desc.IsEnumerable()) { + if (kind == PropertyKind::KEY) { + properties->Set(thread, index++, key); + } else { + OperationResult result = + JSTaggedValue::GetProperty(thread, JSHandle::Cast(obj), key); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + JSHandle value = result.GetValue(); + if (kind == PropertyKind::VALUE) { + properties->Set(thread, index++, value); + } else { + ASSERT_PRINT(kind == PropertyKind::KEY_VALUE, "kind is invalid"); + JSHandle keyValue = factory->NewTaggedArray(2); // 2: key-value pair + keyValue->Set(thread, 0, key.GetTaggedValue()); + keyValue->Set(thread, 1, value.GetTaggedValue()); + JSHandle entry = JSArray::CreateArrayFromList(thread, keyValue); + properties->Set(thread, index++, entry.GetTaggedValue()); + } + } + } + } + } + // 5. Return properties. + return properties; +} + +JSHandle JSObject::GetFunctionRealm(JSThread *thread, const JSHandle &object) +{ + // 1. Assert: obj is a callable object. + ASSERT(object->IsCallable()); + // 2. If obj has a [[Realm]] internal slot, then return obj’s [[Realm]] internal slot. + // 3. If obj is a Bound Function exotic object, then + if (object->IsBoundFunction()) { + // a. Let target be obj’s [[BoundTargetFunction]] internal slot. + JSHandle target(thread, JSHandle(object)->GetBoundTarget()); + // b. Return GetFunctionRealm(target). + return GetFunctionRealm(thread, target); + } + // 4. If obj is a Proxy exotic object, then + if (object->IsJSProxy()) { + // a. If the value of the [[ProxyHandler]] internal slot of obj is null, throw a TypeError exception. + if (JSHandle(object)->GetHandler().IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSObject::GetFunctionRealm: handler is null", + JSHandle(thread, JSTaggedValue::Exception())); + } + // b. Let proxyTarget be the value of obj’s [[ProxyTarget]] internal slot. + JSHandle proxyTarget(thread, JSHandle(object)->GetTarget()); + return GetFunctionRealm(thread, proxyTarget); + } + JSTaggedValue maybeGlobalEnv = JSHandle(object)->GetLexicalEnv(); + if (maybeGlobalEnv.IsUndefined()) { + return thread->GetEcmaVM()->GetGlobalEnv(); + } + while (!maybeGlobalEnv.IsJSGlobalEnv()) { + maybeGlobalEnv = LexicalEnv::Cast(maybeGlobalEnv.GetTaggedObject())->GetParentEnv(); + } + return JSHandle(thread, maybeGlobalEnv); +} + +bool JSObject::InstanceOf(JSThread *thread, const JSHandle &object, + const JSHandle &target) +{ + // 1. If Type(target) is not Object, throw a TypeError exception. + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when type of target is not Object", false); + } + + EcmaVM *vm = thread->GetEcmaVM(); + // 2. Let instOfHandler be GetMethod(target, @@hasInstance). + JSHandle instOfHandler = GetMethod(thread, target, vm->GetGlobalEnv()->GetHasInstanceSymbol()); + + // 3. ReturnIfAbrupt(instOfHandler). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 4. If instOfHandler is not undefined, then + if (!instOfHandler->IsUndefined()) { + // a. Return ! ToBoolean(? Call(instOfHandler, target, «object»)). + JSHandle arguments = vm->GetFactory()->NewTaggedArray(1); + arguments->Set(thread, 0, object.GetTaggedValue()); + JSTaggedValue tagged = JSFunction::Call(thread, instOfHandler, target, arguments); + return tagged.ToBoolean(); + } + + // 5. If IsCallable(target) is false, throw a TypeError exception. + if (!target->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when target is not Callable", false); + } + + // 6. Return ? OrdinaryHasInstance(target, object). + return JSFunction::OrdinaryHasInstance(thread, target, object); +} + +// ecma6.0 6.2.4.4 +JSHandle JSObject::FromPropertyDescriptor(JSThread *thread, const PropertyDescriptor &desc) +{ + // 1. If Desc is undefined, return undefined + if (desc.IsEmpty()) { + return JSHandle(thread, JSTaggedValue::Undefined()); + } + + // 2. Let obj be ObjectCreate(%ObjectPrototype%). + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle objFunc = env->GetObjectFunction(); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + + auto globalConst = thread->GlobalConstants(); + // 4. If Desc has a [[Value]] field, then Perform CreateDataProperty(obj, "value", Desc.[[Value]]). + if (desc.HasValue()) { + JSHandle valueStr = globalConst->GetHandledValueString(); + bool success = CreateDataProperty(thread, objHandle, valueStr, desc.GetValue()); + RASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 5. If Desc has a [[Writable]] field, then Perform CreateDataProperty(obj, "writable", Desc.[[Writable]]). + if (desc.HasWritable()) { + JSHandle writableStr = globalConst->GetHandledWritableString(); + JSHandle writable(thread, JSTaggedValue(desc.IsWritable())); + [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, writableStr, writable); + ASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 6. If Desc has a [[Get]] field, then Perform CreateDataProperty(obj, "get", Desc.[[Get]]). + if (desc.HasGetter()) { + JSHandle getStr = globalConst->GetHandledGetString(); + bool success = CreateDataProperty(thread, objHandle, getStr, desc.GetGetter()); + RASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 7. If Desc has a [[Set]] field, then Perform CreateDataProperty(obj, "set", Desc.[[Set]]) + if (desc.HasSetter()) { + JSHandle setStr = globalConst->GetHandledSetString(); + bool success = CreateDataProperty(thread, objHandle, setStr, desc.GetSetter()); + RASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 8. If Desc has an [[Enumerable]] field, then Perform CreateDataProperty(obj, "enumerable", + // Desc.[[Enumerable]]). + if (desc.HasEnumerable()) { + JSHandle enumerableStr = globalConst->GetHandledEnumerableString(); + JSHandle enumerable(thread, JSTaggedValue(desc.IsEnumerable())); + [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, enumerableStr, enumerable); + ASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 9. If Desc has a [[Configurable]] field, then Perform CreateDataProperty(obj , "configurable", + // Desc.[[Configurable]]). + if (desc.HasConfigurable()) { + JSHandle configurableStr = globalConst->GetHandledConfigurableString(); + JSHandle configurable(thread, JSTaggedValue(desc.IsConfigurable())); + [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, configurableStr, configurable); + ASSERT_PRINT(success, "CreateDataProperty must be success"); + } + return JSHandle(objHandle); +} + +bool JSObject::ToPropertyDescriptorFast(JSThread *thread, const JSHandle &obj, PropertyDescriptor &desc) +{ + auto *hclass = obj->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (jsType != JSType::JS_OBJECT) { + return false; + } + if (hclass->IsDictionaryMode()) { + return false; + } + auto env = thread->GetEcmaVM()->GetGlobalEnv(); + auto globalConst = thread->GlobalConstants(); + if (hclass->GetPrototype() != env->GetObjectFunctionPrototype().GetTaggedValue()) { + return false; + } + if (JSObject::Cast(hclass->GetPrototype().GetTaggedObject())->GetClass() != + env->GetObjectFunctionPrototypeClass().GetObject()) { + return false; + } + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetAttributes().GetTaggedObject()); + int propsNumber = hclass->GetPropertiesNumber(); + for (int i = 0; i < propsNumber; i++) { + auto attr = layoutInfo->GetAttr(i); + if (attr.IsAccessor()) { + return false; + } + auto key = layoutInfo->GetKey(i); + auto value = JSObject::Cast(obj->GetTaggedObject())->GetProperty(hclass, attr); + if (key == globalConst->GetEnumerableString()) { + bool enumerable = value.ToBoolean(); + desc.SetEnumerable(enumerable); + } else if (key == globalConst->GetConfigurableString()) { + bool configurable = value.ToBoolean(); + desc.SetConfigurable(configurable); + } else if (key == globalConst->GetValueString()) { + auto handleValue = JSHandle(thread, value); + desc.SetValue(handleValue); + } else if (key == globalConst->GetWritableString()) { + bool writable = value.ToBoolean(); + desc.SetWritable(writable); + } else if (key == globalConst->GetGetString()) { + if (!value.IsCallable()) { + return false; + } + auto getter = JSHandle(thread, value); + desc.SetGetter(getter); + } else if (key == globalConst->GetSetString()) { + if (!value.IsCallable()) { + return false; + } + auto setter = JSHandle(thread, value); + desc.SetSetter(setter); + } + } + + if (desc.IsAccessorDescriptor()) { + // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception. + if (desc.HasValue() || desc.HasWritable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "either Value or Writable is present", true); + } + } + return true; +} + +// ecma6.0 6.2.4.5 ToPropertyDescriptor ( Obj ) +void JSObject::ToPropertyDescriptor(JSThread *thread, const JSHandle &obj, PropertyDescriptor &desc) +{ + if (!obj->IsECMAObject()) { + // 2. If Type(Obj) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR(thread, "ToPropertyDescriptor error obj is not Object"); + } + + if (ToPropertyDescriptorFast(thread, obj, desc)) { + return; + } + auto globalConst = thread->GlobalConstants(); + // 3. Let desc be a new Property Descriptor that initially has no fields. + // 4. Let hasEnumerable be HasProperty(Obj, "enumerable") + JSHandle enumerableStr = globalConst->GetHandledEnumerableString(); + { + ObjectOperator op(thread, obj.GetTaggedValue(), enumerableStr.GetTaggedValue()); + if (op.IsFound()) { + auto value = op.FastGetValue(); + bool enumerable = value->IsException() ? false : value->ToBoolean(); + desc.SetEnumerable(enumerable); + } + } + // 7. Let hasConfigurable be HasProperty(Obj, "configurable"). + JSHandle configurableStr = globalConst->GetHandledConfigurableString(); + { + ObjectOperator op(thread, obj.GetTaggedValue(), configurableStr.GetTaggedValue()); + if (op.IsFound()) { + auto value = op.FastGetValue(); + bool conf = value->IsException() ? false : value->ToBoolean(); + desc.SetConfigurable(conf); + } + } + // 10. Let hasValue be HasProperty(Obj, "value"). + JSHandle valueStr = globalConst->GetHandledValueString(); + { + ObjectOperator op(thread, obj.GetTaggedValue(), valueStr.GetTaggedValue()); + if (op.IsFound()) { + JSHandle prop = op.FastGetValue(); + desc.SetValue(prop); + } + } + // 13. Let hasWritable be HasProperty(Obj, "writable"). + JSHandle writableStr = globalConst->GetHandledWritableString(); + { + ObjectOperator op(thread, obj.GetTaggedValue(), writableStr.GetTaggedValue()); + if (op.IsFound()) { + auto value = op.FastGetValue(); + bool writable = value->IsException() ? false : value->ToBoolean(); + desc.SetWritable(writable); + } + } + // 16. Let hasGet be HasProperty(Obj, "get"). + JSHandle getStr = globalConst->GetHandledGetString(); + { + ObjectOperator op(thread, obj.GetTaggedValue(), getStr.GetTaggedValue()); + if (op.IsFound()) { + JSHandle getter = op.FastGetValue(); + if (!getter->IsCallable() && !getter->IsUndefined()) { + THROW_TYPE_ERROR(thread, "getter not callable or undefined"); + } + desc.SetGetter(getter); + } + } + + // 19. Let hasSet be HasProperty(Obj, "set"). + JSHandle setStr = globalConst->GetHandledSetString(); + { + ObjectOperator op(thread, obj.GetTaggedValue(), setStr.GetTaggedValue()); + if (op.IsFound()) { + JSHandle setter = op.FastGetValue(); + if (!setter->IsCallable() && !setter->IsUndefined()) { + THROW_TYPE_ERROR(thread, "setter not callable or undefined"); + } + desc.SetSetter(setter); + } + } + + // 22. If either desc.[[Get]] or desc.[[Set]] is present, then + if (desc.IsAccessorDescriptor()) { + // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception. + if (desc.HasValue() || desc.HasWritable()) { + THROW_TYPE_ERROR(thread, "either desc.[[Value]] or desc.[[Writable]] is present"); + } + } + // 23. Return desc. +} + +JSHandle JSObject::SpeciesConstructor(JSThread *thread, const JSHandle &obj, + const JSHandle &defaultConstructort) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // Assert: Type(O) is Object. + ASSERT_PRINT(obj->IsECMAObject(), "obj must be js object"); + + // Let C be Get(O, "constructor"). + JSHandle contructorKey = globalConst->GetHandledConstructorString(); + JSHandle objConstructor(GetProperty(thread, JSHandle(obj), contructorKey).GetValue()); + // ReturnIfAbrupt(C). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + // If C is undefined, return defaultConstructor. + if (objConstructor->IsUndefined()) { + return defaultConstructort; + } + // If Type(C) is not Object, throw a TypeError exception. + if (!objConstructor->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is not Object", + JSHandle(thread, JSTaggedValue::Exception())); + } + // Let S be Get(C, @@species). + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesConstructor(GetProperty(thread, objConstructor, speciesSymbol).GetValue()); + // ReturnIfAbrupt(S). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + // If S is either undefined or null, return defaultConstructor. + if (speciesConstructor->IsUndefined() || speciesConstructor->IsNull()) { + return defaultConstructort; + } + // If IsConstructor(S) is true, return S. + if (speciesConstructor->IsConstructor()) { + return speciesConstructor; + } + // Throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Constructor", + JSHandle(thread, JSTaggedValue::Exception())); + return JSHandle(thread, JSTaggedValue::Exception()); +} + +// 6.2.4.6 CompletePropertyDescriptor ( Desc ) +void PropertyDescriptor::CompletePropertyDescriptor(const JSThread *thread, PropertyDescriptor &desc) +{ + // 1. ReturnIfAbrupt(Desc). + // 2. Assert: Desc is a Property Descriptor + // 3. Let like be Record{[[Value]]: undefined, [[Writable]]: false, [[Get]]: undefined, [[Set]]: undefined, + // [[Enumerable]]: false, [[Configurable]]: false}. + // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then + if (!desc.IsAccessorDescriptor()) { + // a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to like.[[Value]]. + // b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]] to like.[[Writable]]. + if (!desc.HasValue()) { + desc.SetValue(JSHandle(thread, JSTaggedValue::Undefined())); + } + if (!desc.HasWritable()) { + desc.SetWritable(false); + } + } else { + // a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to like.[[Get]]. + // b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to like.[[Set]]. + // Default value of Get and Set is undefined. + } + // 6. If Desc does not have an [[Enumerable]] field, set Desc.[[Enumerable]] to like.[[Enumerable]]. + // 7. If Desc does not have a [[Configurable]] field, set Desc.[[Configurable]] to like.[[Configurable]]. + if (!desc.HasEnumerable()) { + desc.SetEnumerable(false); + } + if (!desc.HasConfigurable()) { + desc.SetConfigurable(false); + } +} + +// 13.7.5.15 EnumerateObjectProperties ( O ) +JSHandle JSObject::EnumerateObjectProperties(JSThread *thread, const JSHandle &obj) +{ + // 1. Return an Iterator object (25.1.1.2) whose next method iterates over all the String-valued keys of + // enumerable properties of O. The Iterator object must inherit from %IteratorPrototype% (25.1.2). The + // mechanics and order of enumerating the properties is not specified but must conform to the rules specified + // below. + JSHandle object; + if (obj->IsString()) { + object = JSHandle::Cast(JSPrimitiveRef::StringCreate(thread, obj)); + } else { + object = JSTaggedValue::ToPrototypeOrObj(thread, obj); + } + + return thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(object); +} + +void JSObject::DefinePropertyByLiteral(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + auto attr = PropertyAttributes(PropertyAttributes::GetDefaultAttributes()); + if (value->IsAccessorData()) { + attr.SetIsAccessor(true); + } + + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + AddElementInternal(thread, obj, index, value, attr); + return; + } + UNREACHABLE(); +} + +void JSObject::DefineSetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ObjectOperator op(thread, obj, key, OperatorType::OWN); + ASSERT(op.IsFound()); + op.DefineSetter(value); +} + +void JSObject::DefineGetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ObjectOperator op(thread, obj, key, OperatorType::OWN); + ASSERT(op.IsFound()); + op.DefineGetter(value); +} + +JSHandle JSObject::CreateObjectFromLiteral(const JSThread *thread, const JSHandle &literal, + size_t length, bool useDict) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle propsKey = factory->NewTaggedArray(length / 2); // 2: half + JSHandle propsValue = factory->NewTaggedArray(length / 2); // 2: half + JSMutableHandle elements(factory->NewTaggedArray(length)); + + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + uint32_t propsLen = 0; + bool transFlag = false; + if (!useDict) { + for (size_t i = 0; i < length; i += 2) { // 2: skip a pair of key and value + key.Update(literal->Get(i)); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + uint32_t elementCap = elements->GetLength(); + if (JSObject::ShouldTransToDict(elementCap, index)) { + transFlag = true; + break; + } + if ((index + 1) > elementCap) { + elements.Update(TaggedArray::SetCapacity(thread, elements, index + 1).GetTaggedValue()); + } + + elements->Set(thread, index, literal->Get(i + 1)); + } else { + propsKey->Set(thread, propsLen, key); + propsValue->Set(thread, propsLen++, literal->Get(i + 1)); + } + } + } + + if (useDict || transFlag) { + propsLen = 0; + for (size_t i = 0; i < length; i += 2) { // 2: skip a pair of key and value + propsKey->Set(thread, propsLen, literal->Get(i)); + propsValue->Set(thread, propsLen++, literal->Get(i + 1)); + } + } + + JSHandle keys = TaggedArray::SetCapacity(thread, propsKey, propsLen); + JSHandle values = TaggedArray::SetCapacity( + thread, propsValue, propsLen > MIN_PROPERTIES_LENGTH ? propsLen : MIN_PROPERTIES_LENGTH); + JSHandle obj = factory->NewJSObjectByClass(keys, values); + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + for (size_t i = 0; i < propsLen; i++) { + if (i < MIN_PROPERTIES_LENGTH) { + obj->SetPropertyInlinedProps(thread, i, values->Get(i)); + } else { + JSHandle newValues = + factory->CopyPartArray(values, MIN_PROPERTIES_LENGTH, values->GetLength()); + obj->SetProperties(thread, newValues); + break; + } + } + + if (!transFlag) { + obj->SetElements(thread, elements); + } + return obj; +} + +JSHandle JSObject::CreateObjectFromProperties(const JSThread *thread, const JSHandle &properties) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + size_t length = properties->GetLength(); + JSHandle propsValue = factory->NewTaggedArray(length / 2); // 2: half + + uint32_t propsLen = 0; + for (size_t i = 0; i < length; i += 2) { // 2: skip a pair of key and value + if (properties->Get(i).IsHole()) { + break; + } + if (propsLen >= MIN_PROPERTIES_LENGTH) { + propsValue->Set(thread, (propsLen - MIN_PROPERTIES_LENGTH), properties->Get(i + 1)); + } + propsLen++; + } + + JSHandle obj = factory->NewJSObjectByClass(properties, propsLen); + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + for (size_t i = 0; i < propsLen; i++) { + if (i >= MIN_PROPERTIES_LENGTH) { + uint32_t nonInlinedPropsNum = propsLen - MIN_PROPERTIES_LENGTH; + JSHandle values = TaggedArray::SetCapacity( + thread, propsValue, + nonInlinedPropsNum > MIN_PROPERTIES_LENGTH ? nonInlinedPropsNum : MIN_PROPERTIES_LENGTH); + obj->SetProperties(thread, values); + break; + } + obj->SetPropertyInlinedProps(thread, i, properties->Get(i * 2 + 1)); // 2: literal contains a pair of key-value + } + return obj; +} + +void JSObject::AddAccessor(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, PropertyAttributes attr) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ASSERT_PRINT(attr.IsAccessor(), "Attr is not AccessorData"); + ObjectOperator op(thread, obj, key, OperatorType::OWN); + ASSERT(!op.IsFound()); + op.AddProperty(JSHandle::Cast(obj), JSHandle(value), attr); +} + +bool JSObject::UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value) +{ + [[maybe_unused]] DisallowGarbageCollection noGc; + NameDictionary *dict = NameDictionary::Cast(GetProperties().GetTaggedObject()); + int entry = dict->FindEntry(key); + if (entry == -1) { + return false; + } + dict->UpdateValue(thread, entry, value); + return true; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_object.h b/ecmascript/js_object.h new file mode 100644 index 0000000000000000000000000000000000000000..3c71344e2e8fd1deea2b56582071d702b9e380ec --- /dev/null +++ b/ecmascript/js_object.h @@ -0,0 +1,641 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSOBJECT_H +#define PANDA_RUNTIME_ECMASCRIPT_JSOBJECT_H + +#include + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ic/property_box.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/mem/heap_roots.h" +#include "ecmascript/mem/slots.h" +#include "ecmascript/object_operator.h" +#include "ecmascript/property_attributes.h" +#include "ecmascript/tagged_array.h" + +namespace panda { +namespace ecmascript { +class ObjectOperator; + +class JSFunction; +class AccessorData; +class JSArray; + +class JSForInIterator; + +class LexicalEnv; + +// Integrity level for objects +enum IntegrityLevel { SEALED, FROZEN }; + +enum PositionKind { UNKNOWN = 0, INDEXED_PROPERTY = 1, INLINE_NAMED_PROPERTY = 2, OUT_NAMED_PROPERTY = 3 }; +enum PropertyKind { KEY = 0, VALUE, KEY_VALUE }; + +// ecma6.0 6.2.4 The Property Descriptor Specification Type +class PropertyDescriptor final { +public: + explicit PropertyDescriptor() = delete; + + ~PropertyDescriptor() = default; + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(PropertyDescriptor); + DEFAULT_COPY_SEMANTIC(PropertyDescriptor); + + explicit PropertyDescriptor(const JSThread *thread) : thread_(thread) {} + + explicit PropertyDescriptor(const JSThread *thread, JSHandle v) : thread_(thread), value_(v) {} + + explicit PropertyDescriptor(const JSThread *thread, JSHandle v, bool w, bool e, bool c) + : thread_(thread), + writable_(w), + enumerable_(e), + configurable_(c), + hasWritable_(true), + hasEnumerable_(true), + hasConfigurable_(true), + value_(v) + { + } + + explicit PropertyDescriptor(const JSThread *thread, bool w, bool e, bool c) + : PropertyDescriptor(thread, JSHandle(), w, e, c) + { + } + + inline JSHandle GetValue() const + { + if (value_.IsEmpty()) { + return JSHandle(thread_, JSTaggedValue::Undefined()); + } + return value_; + } + + inline void SetValue(JSHandle value) + { + value_ = value; + } + + inline bool IsWritable() const + { + return writable_; + } + + inline void SetWritable(bool flag) + { + writable_ = flag; + hasWritable_ = true; + } + + inline bool IsEnumerable() const + { + return enumerable_; + } + + inline void SetEnumerable(bool flag) + { + enumerable_ = flag; + hasEnumerable_ = true; + } + + inline bool IsConfigurable() const + { + return configurable_; + } + + inline void SetConfigurable(bool flag) + { + configurable_ = flag; + hasConfigurable_ = true; + } + + inline bool HasValue() const + { + return !value_.IsEmpty(); + } + + inline bool HasWritable() const + { + return hasWritable_; + } + + inline bool HasConfigurable() const + { + return hasConfigurable_; + } + + inline bool HasEnumerable() const + { + return hasEnumerable_; + } + + inline bool HasGetter() const + { + return !getter_.IsEmpty(); + } + + inline bool HasSetter() const + { + return !setter_.IsEmpty(); + } + + inline JSHandle GetGetter() const + { + if (getter_->IsNull()) { + return JSHandle(thread_, JSTaggedValue::Undefined()); + } + return getter_; + } + + inline JSHandle GetSetter() const + { + if (setter_->IsNull()) { + return JSHandle(thread_, JSTaggedValue::Undefined()); + } + return setter_; + } + + inline void SetGetter(JSHandle value) + { + getter_ = value; + } + + inline void SetSetter(JSHandle value) + { + setter_ = value; + } + + // 6.2.4.1 + inline bool IsAccessorDescriptor() const + { + // 2. If both Desc.[[Get]] and Desc.[[Set]] are absent, return false. + return !(getter_.IsEmpty() && setter_.IsEmpty()); + } + + inline bool IsDataDescriptor() const + { + // 2. If both Desc.[[Value]] and Desc.[[Writable]] are absent, return false. + return !(value_.IsEmpty() && !hasWritable_); + } + + inline bool IsGenericDescriptor() const + { + // 2. If IsAccessorDescriptor(Desc) and IsDataDescriptor(Desc) are both false, return true + return !IsAccessorDescriptor() && !IsDataDescriptor(); + } + + inline bool IsEmpty() const + { + return !hasWritable_ && !hasEnumerable_ && !hasConfigurable_ && !HasValue() && !HasGetter() && !HasSetter(); + } + + static void CompletePropertyDescriptor(const JSThread *thread, PropertyDescriptor &desc); + +private: + const JSThread *thread_{nullptr}; + + bool writable_{false}; + bool enumerable_{false}; + bool configurable_{false}; + bool hasWritable_{false}; + bool hasEnumerable_{false}; + bool hasConfigurable_{false}; + + JSHandle value_{}; + JSHandle getter_{}; + JSHandle setter_{}; +}; + +enum class ElementTypes { ALLTYPES, STRING_AND_SYMBOL }; + +class PropertyMetaData { +public: + using IsFoundField = BitField; + using IsInlinedPropsField = IsFoundField::NextFlag; + using RepresentationField = IsInlinedPropsField::NextField; + using OffsetField = RepresentationField::NextField; + + explicit PropertyMetaData(uint32_t metaData) : metaData_(metaData) {} + + ~PropertyMetaData() = default; + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(PropertyMetaData); + DEFAULT_COPY_SEMANTIC(PropertyMetaData); + + explicit PropertyMetaData(bool isFound) + { + SetFound(isFound); + } + + inline bool IsFound() const + { + return IsFoundField::Get(metaData_); + } + + inline void SetFound(bool flag) + { + IsFoundField::Set(flag, &metaData_); + } + + inline bool GetIsInlinedProps() const + { + return IsInlinedPropsField::Get(metaData_); + } + + inline void SetIsInlinedProps(bool flag) + { + IsInlinedPropsField::Set(flag, &metaData_); + } + + inline Representation GetRepresentation() const + { + return RepresentationField::Get(metaData_); + } + + inline void SetRepresentation(Representation representation) + { + RepresentationField::Set(representation, &metaData_); + } + + inline void SetOffset(uint32_t offset) + { + OffsetField::Set(offset, &metaData_); + } + + inline uint32_t GetOffset() const + { + return OffsetField::Get(metaData_); + } + +private: + uint32_t metaData_{0}; +}; + +class OperationResult { +public: + explicit OperationResult(const JSThread *thread, JSTaggedValue value, PropertyMetaData metaData) + : metaData_(metaData) + { + thread_ = thread; + value_ = JSHandle(thread_, value); + } + + ~OperationResult() = default; + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(OperationResult); + DEFAULT_COPY_SEMANTIC(OperationResult); + + JSHandle GetValue() const + { + if (value_->IsPropertyBox()) { + return JSHandle(thread_, + PropertyBox::Cast(value_.GetTaggedValue().GetTaggedObject())->GetValue()); + } + return value_; + } + + JSHandle GetRawValue() const + { + return value_; + } + + const PropertyMetaData &GetPropertyMetaData() const + { + return metaData_; + } + +private: + const JSThread *thread_{nullptr}; + JSHandle value_{}; + PropertyMetaData metaData_{0U}; +}; + +class ECMAObject : public TaggedObject { +public: + static ECMAObject *Cast(ObjectHeader *object); + void SetBuiltinsCtorMode(); + bool IsBuiltinsConstructor() const; + void SetCallable(bool flag); + bool IsCallable() const; + JSMethod *GetCallTarget() const; + + static constexpr size_t HASH_OFFSET = sizeof(TaggedObject); + static constexpr size_t SIZE = HASH_OFFSET + sizeof(TaggedObject); + void SetHash(int32_t hash) + { + Barriers::SetDynPrimitive(this, HASH_OFFSET, hash); + } + int32_t GetHash() const + { + return Barriers::GetDynValue(this, HASH_OFFSET); + } + + void Visitor([[maybe_unused]] const EcmaObjectRangeVisitor &visitor) const + { + // no field in this object + } + + void VisitObjects([[maybe_unused]] const EcmaObjectRangeVisitor &visitor) const + { + // no field in this object + } +}; + +class JSObject : public ECMAObject { +public: + static constexpr int MIN_ELEMENTS_LENGTH = 16; + static constexpr int MIN_PROPERTIES_LENGTH = JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS; + static constexpr int PROPERTIES_GROW_SIZE = 4; + static constexpr int FAST_ELEMENTS_FACTOR = 3; + static constexpr int MIN_GAP = 256; + static constexpr int MAX_GAP = 1024; + static constexpr uint32_t MAX_ELEMENT_INDEX = std::numeric_limits::max(); + + static inline JSObject *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsECMAObject()); + return static_cast(object); + } + + static inline JSObject *Cast(JSTaggedValue value) + { + ASSERT(value.IsHeapObject() && value.GetTaggedObject()->GetClass()->IsECMAObject()); + return static_cast(value.GetTaggedObject()); + } + + // ecma6.0 6.2.4.4 + static JSHandle FromPropertyDescriptor(JSThread *thread, const PropertyDescriptor &desc); + + // ecma6.0 6.2.4.5 ToPropertyDescriptor ( Obj ) + static void ToPropertyDescriptor(JSThread *thread, const JSHandle &obj, PropertyDescriptor &desc); + static bool ToPropertyDescriptorFast(JSThread *thread, const JSHandle &obj, + PropertyDescriptor &desc); + + // ecma6 7.3 Operations on Objects + static JSHandle GetMethod(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static bool CreateDataProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value); + + static bool CreateDataProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value); + + static bool CreateMethodProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value); + + static bool CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value); + + static bool CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value); + + static JSHandle EnumerableOwnNames(JSThread *thread, const JSHandle &obj); + + // 7.3.23 EnumerableOwnPropertyNames ( O, kind ) + static JSHandle EnumerableOwnPropertyNames(JSThread *thread, const JSHandle &obj, + PropertyKind kind); + + static JSHandle GetFunctionRealm(JSThread *thread, const JSHandle &object); + + static bool SetIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level); + + static bool TestIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level); + + static JSHandle SpeciesConstructor(JSThread *thread, const JSHandle &obj, + const JSHandle &defaultConstructort); + // 7.3.17 + template + static JSHandle CreateListFromArrayLike(JSThread *thread, const JSHandle &obj); + + // emca6 9.1 + // [[GetPrototypeOf]] + JSTaggedValue GetPrototype(JSThread *thread) const; + + // [[SetPrototypeOf]] + static bool SetPrototype(JSThread *thread, const JSHandle &obj, const JSHandle &proto); + + // [[IsExtensible]] + bool IsExtensible() const; + + // [[PreventExtensions]] + static bool PreventExtensions(JSThread *thread, const JSHandle &obj); + + // [[GetOwnProperty]] -> Undefined | Property Descriptor + static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + PropertyDescriptor &desc); + + static bool GlobalGetOwnProperty(JSThread *thread, const JSHandle &key, PropertyDescriptor &desc); + + static bool OrdinaryGetOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc); + + // [[DefineOwnProperty]] + static bool DefineOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const PropertyDescriptor &desc); + + static bool DefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const PropertyDescriptor &desc); + + static bool OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc); + + static bool OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const PropertyDescriptor &desc); + + static bool IsCompatiblePropertyDescriptor(bool extensible, const PropertyDescriptor &desc, + const PropertyDescriptor ¤t); + + static bool ValidateAndApplyPropertyDescriptor(ObjectOperator *op, bool extensible, const PropertyDescriptor &desc, + const PropertyDescriptor ¤t); + + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &receiver); + + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, uint32_t index); + + static OperationResult GetPropertyFromGlobal(JSThread *thread, const JSHandle &key); + + static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, bool mayThrow = false); + + static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, bool mayThrow = false); + + static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver, + bool mayThrow = false); + + static bool SetProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value, bool mayThrow = false); + + static bool GlobalSetProperty(JSThread *thread, const JSHandle &key, + const JSHandle &value, bool mayThrow); + + // [[HasProperty]] + static bool HasProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); + + static bool HasProperty(JSThread *thread, const JSHandle &obj, uint32_t index); + + // 9.1.10 [[Delete]] + static bool DeleteProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); + + // [[OwnPropertyKeys]] + static JSHandle GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj); + + // 9.1.13 ObjectCreate + static JSHandle ObjectCreate(JSThread *thread, const JSHandle &proto); + + // 12.9.4 Runtime Semantics: InstanceofOperator(O, C) + static bool InstanceOf(JSThread *thread, const JSHandle &object, + const JSHandle &target); + + // 13.7.5.15 EnumerateObjectProperties ( O ); same as [[Enumerate]] + static JSHandle EnumerateObjectProperties(JSThread *thread, const JSHandle &obj); + + static bool IsRegExp(JSThread *thread, const JSHandle &argument); + + static JSTaggedValue CallGetter(JSThread *thread, const AccessorData *accessor, + const JSHandle &receiver); + static bool CallSetter(JSThread *thread, const AccessorData &accessor, const JSHandle &receiver, + const JSHandle &value, bool mayThrow = false); + + void FillElementsWithHoles(const JSThread *thread, uint32_t start, uint32_t end); + + JSHClass *GetJSHClass() const; + bool IsJSGlobalObject() const; + bool IsConstructor() const; + bool IsECMAObject() const; + bool IsJSError() const; + bool IsArguments() const; + bool IsDate() const; + bool IsJSArray() const; + bool IsJSMap() const; + bool IsJSSet() const; + bool IsJSRegExp() const; + bool IsJSFunction() const; + bool IsBoundFunction() const; + bool IsProxyRevocFunction() const; + bool IsAccessorData() const; + bool IsJSGlobalEnv() const; + bool IsJSProxy() const; + bool IsGeneratorObject() const; + bool IsForinIterator() const; + bool IsJSSetIterator() const; + bool IsJSMapIterator() const; + bool IsJSArrayIterator() const; + bool IsJSPrimitiveRef() const; + bool IsElementDict() const; + bool IsPropertiesDict() const; + bool IsTypedArray() const; + + static void DefinePropertyByLiteral(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value); + static void DefineSetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value); + static void DefineGetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value); + static JSHandle CreateObjectFromLiteral(const JSThread *thread, const JSHandle &literal, + size_t length, bool useDict); + static JSHandle CreateObjectFromProperties(const JSThread *thread, + const JSHandle &properties); + static void GetAllKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray); + static void GetAllKeys(const JSThread *thread, const JSHandle &obj, + std::vector &keyVector); + static void GetAllElementKeys(JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray); + static void GetALLElementKeysIntoVector(const JSThread *thread, const JSHandle &obj, + std::vector &keyVector); + uint32_t GetNumberOfKeys(); + uint32_t GetNumberOfElements(); + + static JSHandle GetEnumElementKeys(JSThread *thread, const JSHandle &obj, int offset, + uint32_t numOfElements, array_size_t *keys); + static JSHandle GetAllEnumKeys(const JSThread *thread, const JSHandle &obj, int offset, + uint32_t numOfKeys, array_size_t *keys); + + static void AddAccessor(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, PropertyAttributes attr); + + static constexpr size_t PROPERTIES_OFFSET = ECMAObject::SIZE; + + ACCESSORS(Properties, PROPERTIES_OFFSET, ELEMENTS_OFFSET); + ACCESSORS(Elements, ELEMENTS_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(ECMAObject, PROPERTIES_OFFSET, SIZE) + + DECL_DUMP() + + static JSHandle TransitionToDictionary(const JSThread *thread, const JSHandle &receiver); + + inline void SetPropertyInlinedProps(const JSThread *thread, uint32_t index, JSTaggedValue value); + inline void SetPropertyInlinedProps(const JSThread *thread, const JSHClass *hclass, uint32_t index, + JSTaggedValue value); + inline JSTaggedValue GetPropertyInlinedProps(uint32_t index) const; + inline JSTaggedValue GetPropertyInlinedProps(const JSHClass *hclass, uint32_t index) const; + inline JSTaggedValue GetProperty(const JSHClass *hclass, PropertyAttributes attr) const; + inline void SetProperty(const JSThread *thread, const JSHClass *hclass, PropertyAttributes attr, + JSTaggedValue value); + + static bool IsArrayLengthWritable(JSThread *thread, const JSHandle &receiver); + bool UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value); + static bool ShouldTransToDict(uint32_t capacity, uint32_t index); + static JSHandle GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, + uint32_t capacity); + + inline uint32_t GetPropertyInObjectIndex(uint32_t index) const + { + ASSERT(index < JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + size_t offset = GetJSHClass()->GetObjectSize() - + (JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS - index) * JSTaggedValue::TaggedTypeSize(); + return offset / JSTaggedValue::TaggedTypeSize(); + } + +protected: + static void ElementsToDictionary(const JSThread *thread, JSHandle obj); + +private: + friend class ObjectOperator; + friend class LoadICRuntime; + friend class StoreICRuntime; + friend class FastRuntimeStub; + friend class ICRuntimeStub; + + static bool AddElementInternal( + JSThread *thread, const JSHandle &receiver, uint32_t index, const JSHandle &value, + PropertyAttributes attr = PropertyAttributes(PropertyAttributes::GetDefaultAttributes())); + + static JSTaggedValue GetProperty(JSThread *thread, ObjectOperator *op); + static bool SetProperty(ObjectOperator *op, const JSHandle &value, bool mayThrow); + static void DeletePropertyInternal(JSThread *thread, const JSHandle &obj, + const JSHandle &key, uint32_t index); + int FindProperty(const JSHandle &key); + + static uint32_t ComputeElementCapacity(uint32_t oldCapacity); + static uint32_t ComputePropertyCapacity(uint32_t oldCapacity); +}; +} // namespace ecmascript +} // namespace panda + +#endif diff --git a/ecmascript/js_primitive_ref.cpp b/ecmascript/js_primitive_ref.cpp new file mode 100644 index 0000000000000000000000000000000000000000..acbb15fb963b0db380a08e8066fa0bfa7714cd83 --- /dev/null +++ b/ecmascript/js_primitive_ref.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_primitive_ref.h" +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/mem/assert_scope-inl.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +// ES6 9.4.3.4 StringCreate( value, prototype) +JSHandle JSPrimitiveRef::StringCreate(JSThread *thread, const JSHandle &value) +{ + // 1. ReturnIfAbrupt(prototype). + // 2. Assert: Type(value) is String. + ASSERT(value->IsString()); + // 3. Let S be a newly created String exotic object. + // 4. Set the [[StringData]] internal slot of S to value. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle str(factory->NewJSString(value)); + // 11. Let length be the number of code unit elements in value. + // 12. Let status be DefinePropertyOrThrow(S, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]: + // false, [[Enumerable]]: false, [[Configurable]]: false }). + JSHandle lengthStr = thread->GlobalConstants()->GetHandledLengthString(); + + int32_t length = EcmaString::Cast(value->GetTaggedObject())->GetLength(); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(length)), false, false, false); + [[maybe_unused]] bool status = JSTaggedValue::DefinePropertyOrThrow(thread, str, lengthStr, desc); + // 13. Assert: status is not an abrupt completion. + // 14. Return S. + ASSERT(status); + return JSHandle(str); +} + +bool JSPrimitiveRef::StringGetIndexProperty(const JSThread *thread, const JSHandle &obj, uint32_t index, + PropertyDescriptor *desc) +{ + uint16_t tmpChar; + { + DISALLOW_GARBAGE_COLLECTION; + EcmaString *str = EcmaString::Cast(JSPrimitiveRef::Cast(*obj)->GetValue().GetTaggedObject()); + if (str->GetLength() <= index) { + return false; + } + // 10. Let resultStr be a String value of length 1, containing one code unit from str, specifically the code + // unit at index index + tmpChar = str->At(index); + } + JSHandle value(thread->GetEcmaVM()->GetFactory()->NewFromUtf16(&tmpChar, 1)); + // 11. Return a PropertyDescriptor{ [[Value]]: resultStr, [[Enumerable]]: true, [[Writable]]: false, + // [[Configurable]]: false }. + desc->SetValue(value); + desc->SetEnumerable(true); + desc->SetWritable(false); + desc->SetConfigurable(false); + return true; +} +// ES6 9.4.3.3 [[OwnPropertyKeys]] ( ) +} // namespace panda::ecmascript diff --git a/ecmascript/js_primitive_ref.h b/ecmascript/js_primitive_ref.h new file mode 100644 index 0000000000000000000000000000000000000000..27b727ed56865ed84fc54b1dff04124091e70e30 --- /dev/null +++ b/ecmascript/js_primitive_ref.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSPRIMITIVEREF_H +#define PANDA_RUNTIME_ECMASCRIPT_JSPRIMITIVEREF_H + +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/tagged_array.h" + +namespace panda::ecmascript { +enum class PrimitiveType : uint8_t { + PRIMITIVE_BOOLEAN = 0, + PRIMITIVE_NUMBER, + PRIMITIVE_STRING, + PRIMITIVE_SYMBOL, +}; + +class JSPrimitiveRef : public JSObject { +public: + static JSPrimitiveRef *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSPrimitiveRef()); + return static_cast(object); + } + + JSPrimitiveRef() = delete; + + bool IsNumber() const + { + return GetValue().IsNumber(); + } + + bool IsBoolean() const + { + return GetValue().IsBoolean(); + } + + bool IsString() const + { + return GetValue().IsString(); + } + + bool IsSymbol() const + { + return GetValue().IsSymbol(); + } + + uint32_t GetStringLength() const + { + ASSERT(IsString()); + return EcmaString::Cast(GetValue().GetTaggedObject())->GetLength(); + } + + // ES6 9.4.3 String Exotic Objects + // ES6 9.4.3.4 StringCreate( value, prototype)// proto always be %StringPrototype% + static JSHandle StringCreate(JSThread *thread, const JSHandle &value); + static bool StringGetIndexProperty(const JSThread *thread, const JSHandle &obj, uint32_t index, + PropertyDescriptor *desc); + + static constexpr size_t VALUE_OFFSET = JSObject::SIZE; + ACCESSORS(Value, VALUE_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VALUE_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSPRIMITIVEREF_H diff --git a/ecmascript/js_promise.cpp b/ecmascript/js_promise.cpp new file mode 100644 index 0000000000000000000000000000000000000000..914a6e12b78ae209a89d7cdb80551d1317bd5793 --- /dev/null +++ b/ecmascript/js_promise.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_promise.h" +#include "ecmascript/base/error_type.h" +#include "ecmascript/builtins/builtins_promise_handler.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/global_env.h" +#include "ecmascript/jobs/micro_job_queue.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +using BuiltinsPromiseHandler = builtins::BuiltinsPromiseHandler; + +JSHandle JSPromise::CreateResolvingFunctions(JSThread *thread, + const JSHandle &promise) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let alreadyResolved be a new Record { [[value]]: false }. + JSHandle record = factory->NewPromiseRecord(); + record->SetValue(thread, JSTaggedValue::False()); + + // 2. Let resolve be a new built-in function object as defined in Promise Resolve Functions (25.4.1.3.2). + JSHandle resolve = + factory->CreateJSPromiseReactionsFunction(reinterpret_cast(BuiltinsPromiseHandler::Resolve)); + // 3. Set the [[Promise]] internal slot of resolve to promise. + resolve->SetPromise(thread, promise); + // 4. Set the [[AlreadyResolved]] internal slot of resolve to alreadyResolved. + resolve->SetAlreadyResolved(thread, record); + // 5. Let reject be a new built-in function object as defined in Promise Reject Functions (25.4.1.3.1). + JSHandle reject = + factory->CreateJSPromiseReactionsFunction(reinterpret_cast(BuiltinsPromiseHandler::Reject)); + // 6. Set the [[Promise]] internal slot of reject to promise. + reject->SetPromise(thread, promise); + // 7. Set the [[AlreadyResolved]] internal slot of reject to alreadyResolved. + reject->SetAlreadyResolved(thread, record); + // 8. Return a new Record { [[Resolve]]: resolve, [[Reject]]: reject }. + JSHandle reactions = factory->NewResolvingFunctionsRecord(); + reactions->SetResolveFunction(thread, resolve.GetTaggedValue()); + reactions->SetRejectFunction(thread, reject.GetTaggedValue()); + return reactions; +} + +JSTaggedValue JSPromise::FulfillPromise(JSThread *thread, const JSHandle &promise, + const JSHandle &value) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 1. Assert: the value of promise's [[PromiseState]] internal slot is "pending". + JSHandle handleStatus(thread, promise->GetPromiseState()); + ASSERT_PRINT(JSTaggedValue::SameValue(handleStatus.GetTaggedValue(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + "FulfillPromise: state must be pending"); + // 2. Let reactions be the value of promise's [[PromiseFulfillReactions]] internal slot. + JSHandle reactions(thread, promise->GetPromiseFulfillReactions()); + // 3. Set the value of promise's [[PromiseResult]] internal slot to value. + promise->SetPromiseResult(thread, value); + // 4. Set the value of promise's [[PromiseFulfillReactions]] internal slot to undefined. + promise->SetPromiseFulfillReactions(thread, globalConst->GetHandledUndefined()); + // 5. Set the value of promise's [[PromiseRejectReactions]] internal slot to undefined. + promise->SetPromiseRejectReactions(thread, globalConst->GetHandledUndefined()); + // 6. Set the value of promise's [[PromiseState]] internal slot to "fulfilled". + promise->SetPromiseState(thread, JSTaggedValue(static_cast(PromiseStatus::FULFILLED))); + // 7. Return TriggerPromiseReactions(reactions, reason). + return TriggerPromiseReactions(thread, reactions, value); +} + +JSHandle JSPromise::NewPromiseCapability(JSThread *thread, const JSHandle &obj) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. If IsConstructor(C) is false, throw a TypeError exception. + if (!obj->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "NewPromiseCapability: obj is not constructor!", + factory->NewPromiseCapability()); + } + // 2. NOTE C is assumed to be a constructor function that supports the parameter conventions of the Promise + // constructor (see 25.4.3.1). + // 3. Let promiseCapability be a new PromiseCapability { [[Promise]]: undefined, [[Resolve]]: undefined, + // [[Reject]]: undefined }. + JSHandle promiseCapability = factory->NewPromiseCapability(); + // 4. Let executor be a new built-in function object as defined in GetCapabilitiesExecutor Functions + // (25.4.1.5.1). + JSHandle executor = + factory->CreateJSPromiseExecutorFunction(reinterpret_cast(BuiltinsPromiseHandler::Executor)); + // 5. Set the [[Capability]] internal slot of executor to promiseCapability. + executor->SetCapability(thread, promiseCapability.GetTaggedValue()); + // 6. Let promise be Construct(C, «executor»). + // 7. ReturnIfAbrupt(promise). + array_size_t length = 1; + JSHandle arrayExecutor = factory->NewTaggedArray(length); + arrayExecutor->Set(thread, 0, executor); + JSHandle newTarget(thread, JSTaggedValue::Undefined()); + JSTaggedValue result = JSFunction::Construct(thread, obj, arrayExecutor, newTarget); + JSHandle promise(thread, result); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, factory->NewPromiseCapability()); + // 8. If IsCallable(promiseCapability.[[Resolve]]) is false, throw a TypeError exception. + if (!promiseCapability->GetResolve().IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "NewPromiseCapability: resolve is not a callable function!", + factory->NewPromiseCapability()); + } + // 9. If IsCallable(promiseCapability.[[Reject]]) is false, throw a TypeError exception. + if (!promiseCapability->GetReject().IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "NewPromiseCapability: reject is not a callable function!", + factory->NewPromiseCapability()); + } + // 10. Set promiseCapability.[[Promise]] to promise. + promiseCapability->SetPromise(thread, promise); + // 11. Return promiseCapability. + return promiseCapability; +} + +bool JSPromise::IsPromise(const JSHandle &value) +{ + // 1. If Type(x) is not Object, return false. + if (!value->IsECMAObject()) { + return false; + } + // 2. If x does not have a [[PromiseState]] internal slot, return false. + if (!value->IsJSPromise()) { + return false; + } + // 3. Return true + return true; +} + +JSTaggedValue JSPromise::RejectPromise(JSThread *thread, const JSHandle &promise, + const JSHandle &reason) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 1. Assert: the value of promise's [[PromiseState]] internal slot is "pending". + JSHandle handleStatus(thread, promise->GetPromiseState()); + + ASSERT_PRINT(JSTaggedValue::SameValue(handleStatus.GetTaggedValue(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + "RejectPromise: state must be pending"); + // 2. Let reactions be the value of promise's [[PromiseRejectReactions]] internal slot. + JSHandle reactions(thread, TaggedQueue::Cast(promise->GetPromiseRejectReactions().GetTaggedObject())); + // 3. Set the value of promise's [[PromiseResult]] internal slot to reason. + promise->SetPromiseResult(thread, reason); + // 4. Set the value of promise's [[PromiseFulfillReactions]] internal slot to undefined. + promise->SetPromiseFulfillReactions(thread, globalConst->GetHandledUndefined()); + // 5. Set the value of promise's [[PromiseRejectReactions]] internal slot to undefined. + promise->SetPromiseRejectReactions(thread, globalConst->GetHandledUndefined()); + // 6. Set the value of promise's [[PromiseState]] internal slot to "rejected". + promise->SetPromiseState(thread, JSTaggedValue(static_cast(PromiseStatus::REJECTED))); + // 7. Return TriggerPromiseReactions(reactions, reason). + return TriggerPromiseReactions(thread, reactions, reason); +} + +JSTaggedValue JSPromise::TriggerPromiseReactions(JSThread *thread, const JSHandle &reactions, + const JSHandle &argument) +{ + // 1. Repeat for each reaction in reactions, in original insertion order + // a. Perform EnqueueJob("PromiseJobs", PromiseReactionJob, «reaction, argument»). + JSHandle job = thread->GetEcmaVM()->GetMicroJobQueue(); + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle promiseReactionsJob(globalEnv->GetPromiseReactionJob()); + JSMutableHandle reaction(thread, JSTaggedValue::Undefined()); + while (!reactions->Empty()) { + reaction.Update(reactions->Pop(thread)); + JSHandle arguments = factory->NewTaggedArray(2); // 2 means the length of new array + arguments->Set(thread, 0, reaction); + arguments->Set(thread, 1, argument); + job->EnqueueJob(thread, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, arguments); + } + // 2. Return undefined. + return globalConst->GetUndefined(); +} + +JSHandle JSPromise::IfThrowGetThrowValue(JSThread *thread) +{ + if (thread->GetException().IsObjectWrapper()) { + JSHandle wrapperValue(thread, thread->GetException()); + JSHandle throwValue(thread, wrapperValue->GetValue()); + return throwValue; + } + return JSHandle(thread, thread->GetException()); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_promise.h b/ecmascript/js_promise.h new file mode 100644 index 0000000000000000000000000000000000000000..430b8c1b7c9af4f631a42ab8e1c0445d4ff3a61c --- /dev/null +++ b/ecmascript/js_promise.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_PROMISE_H +#define PANDA_RUNTIME_ECMASCRIPT_PROMISE_H + +#include "js_object.h" +#include "ecmascript/js_function.h" +#include "ecmascript/tagged_queue.h" +#include "ecmascript/tagged_queue-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/accessor_data.h" +#include "ecmascript/js_tagged_value-inl.h" + +namespace panda::ecmascript { +enum class PromiseStatus : uint32_t { PENDING = 0, FULFILLED, REJECTED }; +enum class PromiseType : uint32_t { RESOLVE = 0, REJECT }; + +class PromiseReaction final : public Record { +public: + static PromiseReaction *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsPromiseReaction()); + return static_cast(object); + } + + static constexpr size_t PROMISE_CAPABILITY_OFFSET = Record::SIZE; + ACCESSORS(PromiseCapability, PROMISE_CAPABILITY_OFFSET, TYPE_OFFSET); + ACCESSORS(Type, TYPE_OFFSET, HANDLER_OFFSET); + ACCESSORS(Handler, HANDLER_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(PROMISE_CAPABILITY_OFFSET, SIZE) +}; + +class PromiseCapability final : public Record { +public: + static PromiseCapability *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsPromiseCapability()); + return static_cast(object); + } + + static constexpr size_t PROMISE_OFFSET = Record::SIZE; + ACCESSORS(Promise, PROMISE_OFFSET, RESOLVE_OFFSET); + ACCESSORS(Resolve, RESOLVE_OFFSET, REJECT_OFFSET); + ACCESSORS(Reject, REJECT_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(PROMISE_OFFSET, SIZE) +}; + +class PromiseIteratorRecord final : public Record { +public: + static PromiseIteratorRecord *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsPromiseIteratorRecord()); + return static_cast(object); + } + + static constexpr size_t ITERATOR_OFFSET = Record::SIZE; + ACCESSORS(Iterator, ITERATOR_OFFSET, DONE_OFFSET); + ACCESSORS(Done, DONE_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(ITERATOR_OFFSET, SIZE) +}; + +class PromiseRecord final : public Record { +public: + static PromiseRecord *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsPromiseRecord()); + return static_cast(object); + } + static constexpr size_t VALUE_OFFSET = Record::SIZE; + ACCESSORS(Value, VALUE_OFFSET, SIZE); + DECL_DUMP() + + DECL_VISIT_OBJECT(VALUE_OFFSET, SIZE) +}; + +class ResolvingFunctionsRecord final : public Record { +public: + static ResolvingFunctionsRecord *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsResolvingFunctionsRecord()); + return static_cast(object); + } + static constexpr size_t RESOLVE_FUNCTION_OFFSET = Record::SIZE; + ACCESSORS(ResolveFunction, RESOLVE_FUNCTION_OFFSET, REJECT_FUNCTION_OFFSET); + ACCESSORS(RejectFunction, REJECT_FUNCTION_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(RESOLVE_FUNCTION_OFFSET, SIZE) +}; + +class JSPromise final : public JSObject { +public: + static JSPromise *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSPromise()); + return static_cast(object); + } + + // ES6 25.4.1.3 CreateResolvingFunctions ( promise ) + static JSHandle CreateResolvingFunctions(JSThread *thread, + const JSHandle &promise); + + // ES6 25.4.1.4 FulfillPromise ( promise, value) + static JSTaggedValue FulfillPromise(JSThread *thread, const JSHandle &promise, + const JSHandle &value); + + // 25.4.1.5 NewPromiseCapability ( C ) + static JSHandle NewPromiseCapability(JSThread *thread, const JSHandle &obj); + + // ES6 24.4.1.6 IsPromise (x) + static bool IsPromise(const JSHandle &value); + + // ES6 25.4.1.7 RejectPromise (promise, reason) + static JSTaggedValue RejectPromise(JSThread *thread, const JSHandle &promise, + const JSHandle &reason); + + // 25.4.1.8 TriggerPromiseReactions (reactions, argument) + static JSTaggedValue TriggerPromiseReactions(JSThread *thread, const JSHandle &reactions, + const JSHandle &argument); + + static JSHandle IfThrowGetThrowValue(JSThread *thread); + + static constexpr size_t PROMISE_STATE_OFFSET = JSObject::SIZE; + ACCESSORS(PromiseState, PROMISE_STATE_OFFSET, PROMISE_RESULT_OFFSET); + ACCESSORS(PromiseResult, PROMISE_RESULT_OFFSET, PROMISE_FULFILL_REACTIONS_OFFSET); + ACCESSORS(PromiseFulfillReactions, PROMISE_FULFILL_REACTIONS_OFFSET, PROMISE_REJECT_REACTIONS_OFFSET); + ACCESSORS(PromiseRejectReactions, PROMISE_REJECT_REACTIONS_OFFSET, PROMISE_IS_HANDLED_OFFSET); + ACCESSORS(PromiseIsHandled, PROMISE_IS_HANDLED_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, PROMISE_STATE_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_PROMISE_H diff --git a/ecmascript/js_proxy.cpp b/ecmascript/js_proxy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..09be113669c866cf90c564a13b0a22e1123eff25 --- /dev/null +++ b/ecmascript/js_proxy.cpp @@ -0,0 +1,922 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_proxy.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +// ES6 9.5.15 ProxyCreate(target, handler) +JSHandle JSProxy::ProxyCreate(JSThread *thread, const JSHandle &target, + const JSHandle &handler) +{ + // 1. If Type(target) is not Object, throw a TypeError exception. + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "ProxyCreate: target is not Object", + JSHandle(thread, JSTaggedValue::Exception())); + } + + // 2. If Type(handler) is not Object, throw a TypeError exception. + if (!handler->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "ProxyCreate: handler is not Object", + JSHandle(thread, JSTaggedValue::Exception())); + } + // 3. Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »). + // 6. If IsCallable(target) is true, then P.[[Call]] as specified in 9.5.12. + + // 8. Set the [[ProxyTarget]] internal slot of P to target. + // 9. Set the [[ProxyHandler]] internal slot of P to handler. + return thread->GetEcmaVM()->GetFactory()->NewJSProxy(target, handler); +} + +// ES6 9.5.1 [[GetPrototypeOf]] ( ) +JSTaggedValue JSProxy::GetPrototype(JSThread *thread, const JSHandle &proxy) +{ + // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O. + JSHandle handler(thread, proxy->GetHandler()); + // 2. If handler is null, throw a TypeError exception. + if (handler->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: handler is null", JSTaggedValue::Exception()); + } + // 3. Assert: Type(handler) is Object. + ASSERT(handler->IsECMAObject()); + // 4. Let target be the value of the [[ProxyTarget]] internal slot of O. + JSHandle targetHandle(thread, proxy->GetTarget()); + // 5. Let trap be GetMethod(handler, "getPrototypeOf"). + JSHandle name(thread->GlobalConstants()->GetHandledGetPrototypeOfString()); + JSHandle trap = JSObject::GetMethod(thread, handler, name); + // 6. ReturnIfAbrupt(trap). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. If trap is undefined, then Return target.[[GetPrototypeOf]](). + if (trap->IsUndefined()) { + return JSHandle(targetHandle)->GetPrototype(thread); + } + // 8. Let handlerProto be Call(trap, handler, «target»). + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1); + args->Set(thread, 0, targetHandle); + JSTaggedValue handlerProto = JSFunction::Call(thread, trap, handler, args); + + // 9. ReturnIfAbrupt(handlerProto). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 10. If Type(handlerProto) is neither Object nor Null, throw a TypeError exception. + if (!handlerProto.IsECMAObject() && !handlerProto.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: Type(handlerProto) is neither Object nor Null", + JSTaggedValue::Exception()); + } + // 11. Let extensibleTarget be IsExtensible(target). + // 12. ReturnIfAbrupt(extensibleTarget). + // 13. If extensibleTarget is true, return handlerProto. + if (targetHandle->IsExtensible(thread)) { + return handlerProto; + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 14. Let targetProto be target.[[GetPrototypeOf]](). + JSTaggedValue targetProto = JSHandle(targetHandle)->GetPrototype(thread); + // 15. ReturnIfAbrupt(targetProto). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 16. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception. + if (!JSTaggedValue::SameValue(handlerProto, targetProto)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: SameValue(handlerProto, targetProto) is false", + JSTaggedValue::Exception()); + } + // 17. Return handlerProto. + return handlerProto; +} + +// ES6 9.5.2 [[SetPrototypeOf]] (V) +bool JSProxy::SetPrototype(JSThread *thread, const JSHandle &proxy, const JSHandle &proto) +{ + // 1. Assert: Either Type(V) is Object or Type(V) is Null. + ASSERT(proto->IsECMAObject() || proto->IsNull()); + // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. + JSTaggedValue handler = proxy->GetHandler(); + // 3. If handler is null, throw a TypeError exception. + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetPrototype: handler is null", false); + } + // 4. Assert: Type(handler) is Object. + ASSERT(handler.IsECMAObject()); + // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. + JSHandle targetHandle(thread, proxy->GetTarget()); + // 6. Let trap be GetMethod(handler, "setPrototypeOf"). + JSHandle name = thread->GlobalConstants()->GetHandledSetPrototypeOfString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 7. If trap is undefined, then Return target.[[SetPrototypeOf]](V). + if (trap->IsUndefined()) { + return JSTaggedValue::SetPrototype(thread, targetHandle, proto); + }; + JSHandle handlerTag(thread, proxy->GetHandler()); + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(2); // 2: target and proto + args->Set(thread, 0, targetHandle); + args->Set(thread, 1, proto); + + JSTaggedValue trapResult = JSFunction::Call(thread, trap, handlerTag, args); + // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, V»)). + // If booleanTrapResult is false, return false + bool booleanTrapResult = trapResult.ToBoolean(); + if (!booleanTrapResult) { + return false; + } + // 10. ReturnIfAbrupt(booleanTrapResult). + // 11. Let extensibleTarget be IsExtensible(target). + // 12. ReturnIfAbrupt(extensibleTarget). + // 13. If extensibleTarget is true, return booleanTrapResult + if (targetHandle->IsExtensible(thread)) { + return booleanTrapResult; + } + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 14. Let targetProto be target.[[GetPrototypeOf]](). + JSTaggedValue targetProto = JSHandle(targetHandle)->GetPrototype(thread); + // 15. ReturnIfAbrupt(targetProto). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 16. If booleanTrapResult is true and SameValue(V, targetProto) is false, throw a TypeError exception. + if (booleanTrapResult && !JSTaggedValue::SameValue(proto.GetTaggedValue(), targetProto)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetPrototype: TypeError of targetProto and Result", false); + } + // 17. Return handlerProto. + return booleanTrapResult; +} + +// ES6 9.5.3 [[IsExtensible]] ( ) +bool JSProxy::IsExtensible(JSThread *thread, const JSHandle &proxy) +{ + // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O. + JSTaggedValue handler = proxy->GetHandler(); + // 2. If handler is null, throw a TypeError exception. + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsExtensible: handler is null", false); + } + // 3. Assert: Type(handler) is Object. + ASSERT(handler.IsECMAObject()); + // 4. Let target be the value of the [[ProxyTarget]] internal slot of O. + JSHandle targetHandle(thread, proxy->GetTarget()); + // 5. Let trap be GetMethod(handler, "isExtensible"). + JSHandle name = thread->GlobalConstants()->GetHandledIsExtensibleString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 6. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 7. If trap is undefined, then Return target.[[IsExtensible]](). + if (trap->IsUndefined()) { + return targetHandle->IsExtensible(thread); + } + // 8. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target»)). + JSHandle newTgt(thread, JSTaggedValue::Undefined()); + JSHandle handlerTag(thread, proxy->GetHandler()); + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1); + args->Set(thread, 0, targetHandle); + JSTaggedValue trapResult = JSFunction::Call(thread, trap, handlerTag, args); + + bool booleanTrapResult = trapResult.ToBoolean(); + // 9. ReturnIfAbrupt(booleanTrapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 10. Let targetResult be target.[[IsExtensible]](). + // 11. ReturnIfAbrupt(targetResult). + // 12. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception. + // 13. Return booleanTrapResult. + if (targetHandle->IsExtensible(thread) != booleanTrapResult) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsExtensible: TypeError of targetResult", false); + } + + return booleanTrapResult; +} + +// ES6 9.5.4 [[PreventExtensions]] ( ) +bool JSProxy::PreventExtensions(JSThread *thread, const JSHandle &proxy) +{ + // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O. + // 2. If handler is null, throw a TypeError exception. + // 3. Assert: Type(handler) is Object. + // 4. Let target be the value of the [[ProxyTarget]] internal slot of O. + // 5. Let trap be GetMethod(handler, "preventExtensions"). + // 6. ReturnIfAbrupt(trap). + // 7. If trap is undefined, then + // a. Return target.[[PreventExtensions]](). + // 8. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target»)). + // 9. ReturnIfAbrupt(booleanTrapResult). + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::PreventExtensions: handler is null", false); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledPreventExtensionsString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 6. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + if (trap->IsUndefined()) { + return JSTaggedValue::PreventExtensions(thread, targetHandle); + } + JSHandle handlerTag(thread, proxy->GetHandler()); + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1); + args->Set(thread, 0, targetHandle); + JSTaggedValue trapResult = JSFunction::Call(thread, trap, handlerTag, args); + + bool booleanTrapResult = trapResult.ToBoolean(); + // 9. ReturnIfAbrupt(booleanTrapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + // 10. If booleanTrapResult is true, then + // a. Let targetIsExtensible be target.[[IsExtensible]](). + // b. ReturnIfAbrupt(targetIsExtensible). + // c. If targetIsExtensible is true, throw a TypeError exception. + if (booleanTrapResult && targetHandle->IsExtensible(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::PreventExtensions: targetIsExtensible is true", false); + } + // 11. Return booleanTrapResult. + return booleanTrapResult; +} + +// ES6 9.5.5 [[GetOwnProperty]] (P) +bool JSProxy::GetOwnProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + PropertyDescriptor &desc) +{ + // 1. Assert: IsPropertyKey(P) is true. + ASSERT(JSTaggedValue::IsPropertyKey(key)); + // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. + // 3. If handler is null, throw a TypeError exception. + // 4. Assert: Type(handler) is Object. + // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. + // 6. Let trap be GetMethod(handler, "getOwnPropertyDescriptor"). + // 7. ReturnIfAbrupt(trap). + // 8. If trap is undefined, then + // a. Return target.[[GetOwnProperty]](P). + // 9. Let trapResultObj be Call(trap, handler, «target, P»). + // 10. ReturnIfAbrupt(trapResultObj). + // 11. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: handler is null", false); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledGetOwnPropertyDescriptorString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + if (trap->IsUndefined()) { + return JSTaggedValue::GetOwnProperty(thread, targetHandle, key, desc); + } + JSHandle handlerTag(thread, proxy->GetHandler()); + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(2); // 2: target and key + args->Set(thread, 0, targetHandle); + args->Set(thread, 1, key); + JSTaggedValue trapResultObj = JSFunction::Call(thread, trap, handlerTag, args); + JSHandle resultHandle(thread, trapResultObj); + + // 11. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception. + if (!trapResultObj.IsECMAObject() && !trapResultObj.IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of trapResultObj", false); + } + // 12. Let targetDesc be target.[[GetOwnProperty]](P). + PropertyDescriptor targetDesc(thread); + bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc); + // 13. ReturnIfAbrupt(targetDesc). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 14. If trapResultObj is undefined, then + if (resultHandle->IsUndefined()) { + // a. If targetDesc is undefined, return undefined. + if (!found) { + return false; + } + // b. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + if (!targetDesc.IsConfigurable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: targetDesc.[[Configurable]] is false", false); + } + // c. Let extensibleTarget be IsExtensible(target). + // d. ReturnIfAbrupt(extensibleTarget). + // e. Assert: Type(extensibleTarget) is Boolean. + // f. If extensibleTarget is false, throw a TypeError exception. + if (!targetHandle->IsExtensible(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: extensibleTarget is false", false); + } + // g. Return undefined. + return false; + } + // 15. Let extensibleTarget be IsExtensible(target). + // 16. ReturnIfAbrupt(extensibleTarget). + // 17. Let resultDesc be ToPropertyDescriptor(trapResultObj). + PropertyDescriptor &resultDesc = desc; + JSObject::ToPropertyDescriptor(thread, resultHandle, resultDesc); + // 18. ReturnIfAbrupt(resultDesc) + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + // 19. Call CompletePropertyDescriptor(resultDesc). + PropertyDescriptor::CompletePropertyDescriptor(thread, resultDesc); + // 20. Let valid be IsCompatiblePropertyDescriptor (extensibleTarget, resultDesc, targetDesc). + bool valid = JSObject::IsCompatiblePropertyDescriptor(targetHandle->IsExtensible(thread), resultDesc, targetDesc); + if (!valid) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of valid", false); + } + // 22. If resultDesc.[[Configurable]] is false, then + if (!resultDesc.IsConfigurable()) { + // a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then + if (!found || targetDesc.IsConfigurable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of targetDesc configurable", false); + } + // b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then + // If targetDesc.[[Writable]] is true, throw a TypeError exception. + if (resultDesc.HasWritable() && !resultDesc.IsWritable() && targetDesc.IsWritable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of targetDesc writable", false); + } + // b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then + // If targetDesc.[[Writable]] is true, throw a TypeError exception. + if (resultDesc.HasWritable() && !resultDesc.IsWritable() && targetDesc.IsWritable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", false); + } + } + // 23. Return resultDesc. + return true; +} + +// ES6 9.5.6 [[DefineOwnProperty]] (P, Desc) +bool JSProxy::DefineOwnProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + const PropertyDescriptor &desc) +{ + // step 1 ~ 10 are almost same as GetOwnProperty + ASSERT(JSTaggedValue::IsPropertyKey(key)); + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: handler is Null", false); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledDefinePropertyString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (trap->IsUndefined()) { + return JSTaggedValue::DefineOwnProperty(thread, targetHandle, key, desc); + } + + // 9. Let descObj be FromPropertyDescriptor(Desc). + JSHandle descObj = JSObject::FromPropertyDescriptor(thread, desc); + JSHandle handlerTag(thread, proxy->GetHandler()); + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(3); // 3: target, key and desc + args->Set(thread, 0, targetHandle); + args->Set(thread, 1, key); + args->Set(thread, 2, descObj); // 2: the third value is desc + JSTaggedValue trapResult = JSFunction::Call(thread, trap, handlerTag, args); + + bool booleanTrapResult = trapResult.ToBoolean(); + // 11. ReturnIfAbrupt(booleanTrapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!booleanTrapResult) { + return false; + } + // 13. Let targetDesc be target.[[GetOwnProperty]](P). + PropertyDescriptor targetDesc(thread); + bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc); + // 14. ReturnIfAbrupt(targetDesc). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 15. Let extensibleTarget be IsExtensible(target). + // 16. ReturnIfAbrupt(extensibleTarget). + // 17. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then Let settingConfigFalse be + // true. + // 18. Else let settingConfigFalse be false. + bool settingConfigFalse = false; + if (desc.HasConfigurable() && !desc.IsConfigurable()) { + settingConfigFalse = true; + } + // 19. If targetDesc is undefined, then + if (!found) { + // a. If extensibleTarget is false, throw a TypeError exception. + if (!targetHandle->IsExtensible(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: extensibleTarget is false", false); + } + // b. If settingConfigFalse is true, throw a TypeError exception. + if (settingConfigFalse) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: settingConfigFalse is true", false); + } + } else { + // a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc , targetDesc) is false, throw a TypeError + // exception. + if (!JSObject::IsCompatiblePropertyDescriptor(targetHandle->IsExtensible(thread), desc, targetDesc)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: CompatiblePropertyDescriptor err", false); + } + // b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception. + if (settingConfigFalse && targetDesc.IsConfigurable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: TypeError of settingConfigFalse", false); + } + // c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] + // is true, then If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception. + if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && targetDesc.IsWritable() && + desc.HasWritable() && !desc.IsWritable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: TypeError of DataDescriptor", false); + } + // c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] + // is true, then If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception. + if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && targetDesc.IsWritable() && + desc.HasWritable() && !desc.IsWritable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", false); + } + } + // 21. Return true. + return true; +} + +// ES6 9.5.7 [[HasProperty]] (P) +bool JSProxy::HasProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key) +{ + // step 1 ~ 10 are almost same as GetOwnProperty + ASSERT(JSTaggedValue::IsPropertyKey(key)); + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: handler is Null", false); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledHasString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (trap->IsUndefined()) { + return JSTaggedValue::HasProperty(thread, targetHandle, key); + } + + // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P»)). + JSHandle handlerTag(thread, proxy->GetHandler()); + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(2); // 2: target and key + args->Set(thread, 0, targetHandle); + args->Set(thread, 1, key); + JSTaggedValue trapResult = JSFunction::Call(thread, trap, handlerTag, args); + + bool booleanTrapResult = trapResult.ToBoolean(); + // 10. ReturnIfAbrupt(booleanTrapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 11. If booleanTrapResult is false, then + if (!booleanTrapResult) { + // a. Let targetDesc be target.[[GetOwnProperty]](P). + PropertyDescriptor targetDesc(thread); + bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc); + // b. ReturnIfAbrupt(targetDesc). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + // c. If targetDesc is not undefined, then + if (found) { + // i. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + if (!targetDesc.IsConfigurable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: TypeError of targetDesc", false); + } + // ii. Let extensibleTarget be IsExtensible(target). + // iii. ReturnIfAbrupt(extensibleTarget). + // iv. If extensibleTarget is false, throw a TypeError exception. + if (!targetHandle->IsExtensible(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: extensibleTarget is false", false); + } + } + } + return booleanTrapResult; +} + +// ES6 9.5.8 [[Get]] (P, Receiver) +OperationResult JSProxy::GetProperty(JSThread *thread, const JSHandle &proxy, + const JSHandle &key, const JSHandle &receiver) +{ + // step 1 ~ 10 are almost same as GetOwnProperty + ASSERT(JSTaggedValue::IsPropertyKey(key)); + JSTaggedValue handler = proxy->GetHandler(); + JSHandle exceptionHandle(thread, JSTaggedValue::Exception()); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetProperty: handler is Null", + OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false))); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledGetString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION( + thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false))); + + if (trap->IsUndefined()) { + return JSTaggedValue::GetProperty(thread, targetHandle, key, receiver); + } + // 9. Let trapResult be Call(trap, handler, «target, P, Receiver»). + JSHandle handlerTag(thread, proxy->GetHandler()); + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(3); // 3: «target, P, Receiver» + args->Set(thread, 0, targetHandle); + args->Set(thread, 1, key); + args->Set(thread, 2, receiver); // 2: the third value is receiver + JSTaggedValue trapResult = JSFunction::Call(thread, trap, handlerTag, args); + JSHandle resultHandle(thread, trapResult); + + // 10. ReturnIfAbrupt(trapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION( + thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false))); + + // 11. Let targetDesc be target.[[GetOwnProperty]](P). + PropertyDescriptor targetDesc(thread); + bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc); + // 12. ReturnIfAbrupt(targetDesc). + RETURN_VALUE_IF_ABRUPT_COMPLETION( + thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false))); + + // 13. If targetDesc is not undefined, then + if (found) { + // a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is + // false, then + if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && !targetDesc.IsWritable()) { + // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception. + if (!JSTaggedValue::SameValue(resultHandle.GetTaggedValue(), targetDesc.GetValue().GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN( + thread, "JSProxy::GetProperty: TypeError of trapResult", + OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false))); + } + } + // b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Get]] is + // undefined, then + if (targetDesc.IsAccessorDescriptor() && !targetDesc.IsConfigurable() && + targetDesc.GetGetter()->IsUndefined()) { + // i. If trapResult is not undefined, throw a TypeError exception. + if (!resultHandle.GetTaggedValue().IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN( + thread, "JSProxy::GetProperty: trapResult is not undefined", + OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false))); + } + } + } + // 14. Return trapResult. + return OperationResult(thread, resultHandle.GetTaggedValue(), PropertyMetaData(true)); +} + +// ES6 9.5.9 [[Set]] ( P, V, Receiver) +bool JSProxy::SetProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver, bool mayThrow) +{ + // step 1 ~ 10 are almost same as GetOwnProperty + ASSERT(JSTaggedValue::IsPropertyKey(key)); + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: handler is Null", false); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledSetString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (trap->IsUndefined()) { + return JSTaggedValue::SetProperty(thread, targetHandle, key, value, receiver, mayThrow); + } + + // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P, V, Receiver»)) + JSHandle handlerTag(thread, proxy->GetHandler()); + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(4); // 4: «target, P, V, Receiver» + args->Set(thread, 0, targetHandle); + args->Set(thread, 1, key); + args->Set(thread, 2, value); // 2: the third value is value + args->Set(thread, 3, receiver); // 3: the fourth value is receiver + JSTaggedValue trapResult = JSFunction::Call(thread, trap, handlerTag, args); + + bool booleanTrapResult = trapResult.ToBoolean(); + // 11. ReturnIfAbrupt(booleanTrapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!booleanTrapResult) { + return false; + } + // 13. Let targetDesc be target.[[GetOwnProperty]](P). + PropertyDescriptor targetDesc(thread); + bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc); + // 14. If targetDesc is not undefined, then + if (found) { + // a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is + // false, then + if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && !targetDesc.IsWritable()) { + // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception. + if (!JSTaggedValue::SameValue(value, targetDesc.GetValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: TypeError of trapResult", false); + } + } + // b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false, then + // i. If targetDesc.[[Set]] is undefined, throw a TypeError exception. + if (targetDesc.IsAccessorDescriptor() && !targetDesc.IsConfigurable() && + targetDesc.GetSetter()->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: TypeError of AccessorDescriptor", false); + } + } + return true; +} + +// ES6 9.5.10 [[Delete]] (P) +bool JSProxy::DeleteProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key) +{ + // step 1 ~ 13 are almost same as GetOwnProperty + ASSERT(JSTaggedValue::IsPropertyKey(key)); + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: handler is Null", false); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledDeletePropertyString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (trap->IsUndefined()) { + return JSTaggedValue::DeleteProperty(thread, targetHandle, key); + } + + // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P»)). + JSHandle newTgt(thread, JSTaggedValue::Undefined()); + JSHandle handlerTag(thread, proxy->GetHandler()); + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(2); // 2: target and key + args->Set(thread, 0, targetHandle); + args->Set(thread, 1, key); + JSTaggedValue trapResult = JSFunction::Call(thread, trap, handlerTag, args); + + bool booleanTrapResult = trapResult.ToBoolean(); + // 11. ReturnIfAbrupt(booleanTrapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!booleanTrapResult) { + return false; + } + // 13. Let targetDesc be target.[[GetOwnProperty]](P). + PropertyDescriptor targetDesc(thread); + bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc); + // 14. If targetDesc is undefined, return true. + if (!found) { + return true; + } + // 15. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + if (!targetDesc.IsConfigurable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: targetDesc is not Configurable", false); + } + if (!targetHandle->IsExtensible(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: targetHandle is not Extensible", false); + } + if (!targetHandle->IsExtensible(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", false); + } + // 16. Return true. + return true; +} + +// ES6 9.5.12 [[OwnPropertyKeys]] () +JSHandle JSProxy::OwnPropertyKeys(JSThread *thread, const JSHandle &proxy) +{ + // step 1 ~ 4 get ProxyHandler and ProxyTarget + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: handler is null", + JSHandle(thread, JSTaggedValue::Exception())); + } + + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + + // 5.Let trap be GetMethod(handler, "ownKeys"). + JSHandle key = thread->GlobalConstants()->GetHandledOwnKeysString(); + JSHandle handlerHandle(thread, handler); + JSHandle trap(JSObject::GetMethod(thread, handlerHandle, key)); + + // 6.ReturnIfAbrupt(trap). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + // 7.If trap is undefined, then + // a.Return target.[[OwnPropertyKeys]](). + if (trap->IsUndefined()) { + return JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle); + } + + // 8.Let trapResultArray be Call(trap, handler, «target»). + JSHandle tagFunc(targetHandle); + JSHandle callArgs = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1); + callArgs->Set(thread, 0, targetHandle); + JSTaggedValue res = JSFunction::Call(thread, trap, handlerHandle, callArgs); + JSHandle trap_res_arr(thread, res); + + // 9.Let trapResult be CreateListFromArrayLike(trapResultArray, «String, Symbol»). + // 10.ReturnIfAbrupt(trapResult) + // If trapResult contains any duplicate entries, throw a TypeError exception. + JSHandle trapRes( + JSObject::CreateListFromArrayLike(thread, trap_res_arr)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + if (trapRes->HasDuplicateEntry()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: contains duplicate entries", + JSHandle(thread, JSTaggedValue::Exception())); + } + + // 11.Let extensibleTarget be IsExtensible(target). + bool extensibleTarget = targetHandle->IsExtensible(thread); + + // 12.ReturnIfAbrupt(extensibleTarget). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + // 13.Let targetKeys be target.[[OwnPropertyKeys]](). + JSHandle targetKeys = JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle); + + // 14.ReturnIfAbrupt(targetKeys). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + // 15.Assert: targetKeys is a List containing only String and Symbol values. + // 16.Let targetConfigurableKeys be an empty List. + // 17.Let targetNonconfigurableKeys be an empty List. + // 18.Repeat, for each element key of targetKeys, + // a.Let desc be target.[[GetOwnProperty]](key). + // b.ReturnIfAbrupt(desc). + // c.If desc is not undefined and desc.[[Configurable]] is false, then + // i.Append key as an element of targetNonconfigurableKeys. + // d.Else, + // i.Append key as an element of targetConfigurableKeys. + array_size_t length = targetKeys->GetLength(); + JSHandle tgtCfigKeys = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length); + JSHandle tgtNoCfigKeys = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length); + + array_size_t cfigLength = 0; + array_size_t noCfigLength = 0; + for (array_size_t i = 0; i < length; i++) { + JSHandle targetKey(thread, targetKeys->Get(i)); + ASSERT(targetKey->IsStringOrSymbol()); + + PropertyDescriptor desc(thread); + JSTaggedValue::GetOwnProperty(thread, targetHandle, targetKey, desc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + if (!desc.IsEmpty() && !desc.IsConfigurable()) { + tgtNoCfigKeys->Set(thread, noCfigLength, targetKey); + noCfigLength++; + } else { + tgtCfigKeys->Set(thread, cfigLength, targetKey); + cfigLength++; + } + } + + // 19.If extensibleTarget is true and targetNonconfigurableKeys is empty, then + // a.Return trapResult. + if (extensibleTarget && (cfigLength == 0)) { + return trapRes; + } + + // 20.Let uncheckedResultKeys be a new List which is a copy of trapResult. + JSHandle uncheckFesKeys = + thread->GetEcmaVM()->GetFactory()->CopyArray(trapRes, trapRes->GetLength(), trapRes->GetLength()); + array_size_t uncheckLength = uncheckFesKeys->GetLength(); + + // 21.Repeat, for each key that is an element of targetNonconfigurableKeys, + // a.If key is not an element of uncheckedResultKeys, throw a TypeError exception. + // b.Remove key from uncheckedResultKeys + for (array_size_t i = 0; i < noCfigLength; i++) { + array_size_t idx = uncheckFesKeys->GetIdx(tgtNoCfigKeys->Get(i)); + if (idx == TaggedArray::MAX_ARRAY_INDEX) { + THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: key is not an element of uncheckedResultKeys", + JSHandle(thread, JSTaggedValue::Exception())); + } + uncheckFesKeys->Set(thread, idx, JSTaggedValue::Hole()); + uncheckLength--; + } + + // 22.If extensibleTarget is true, return trapResult. + if (extensibleTarget) { + return trapRes; + } + + // 23.Repeat, for each key that is an element of targetConfigurableKeys, + // a.If key is not an element of uncheckedResultKeys, throw a TypeError exception. + // b.Remove key from uncheckedResultKeys + for (array_size_t i = 0; i < cfigLength; i++) { + array_size_t idx = uncheckFesKeys->GetIdx(tgtCfigKeys->Get(i)); + if (idx == TaggedArray::MAX_ARRAY_INDEX) { + THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: key is not an element of uncheckedResultKeys", + JSHandle(thread, JSTaggedValue::Exception())); + } + uncheckFesKeys->Set(thread, idx, JSTaggedValue::Hole()); + uncheckLength--; + } + + // 24.If uncheckedResultKeys is not empty, throw a TypeError exception. + if (uncheckLength != 0) { + THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: uncheckedResultKeys is not empty", + JSHandle(thread, JSTaggedValue::Exception())); + } + + // 25.Return trapResult. + return trapRes; +} + +// ES6 9.5.13 [[Call]] (thisArgument, argumentsList) +JSTaggedValue JSProxy::CallInternal(JSThread *thread, const JSHandle &proxy, + const JSHandle &thisArg, const JSHandle &argv) +{ + // step 1 ~ 4 get ProxyHandler and ProxyTarget + JSHandle handler(thread, proxy->GetHandler()); + if (handler->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Call: handler is null", JSTaggedValue::Exception()); + } + ASSERT(handler->IsECMAObject()); + JSHandle target(thread, proxy->GetTarget()); + + // 5.Let trap be GetMethod(handler, "apply"). + JSHandle key(thread->GlobalConstants()->GetHandledApplyString()); + JSHandle method = JSObject::GetMethod(thread, handler, key); + + // 6.ReturnIfAbrupt(trap). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7.If trap is undefined, then + // a.Return Call(target, thisArgument, argumentsList). + if (method->IsUndefined()) { + return JSFunction::Call(thread, target, thisArg, argv); + } + // 8.Let argArray be CreateArrayFromList(argumentsList). + JSHandle arrHandle = JSArray::CreateArrayFromList(thread, argv); + + // 9.Return Call(trap, handler, «target, thisArgument, argArray»). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle nextArgv(factory->NewTaggedArray(3)); // 3: «target, thisArgument, argArray» + nextArgv->Set(thread, 0, target.GetTaggedValue()); + nextArgv->Set(thread, 1, thisArg.GetTaggedValue()); + nextArgv->Set(thread, 2, arrHandle.GetTaggedValue()); // 2: the third value is an args-array + return JSFunction::Call(thread, method, handler, nextArgv); +} + +// ES6 9.5.14 [[Construct]] ( argumentsList, newTarget) +JSTaggedValue JSProxy::ConstructInternal(JSThread *thread, const JSHandle &proxy, + const JSHandle &argv, const JSHandle &newTarget) +{ + // step 1 ~ 4 get ProxyHandler and ProxyTarget + JSHandle handler(thread, proxy->GetHandler()); + if (handler->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor: handler is null", JSTaggedValue::Exception()); + } + ASSERT(handler->IsECMAObject()); + JSHandle target(thread, proxy->GetTarget()); + + // 5.Let trap be GetMethod(handler, "construct"). + JSHandle key(thread->GlobalConstants()->GetHandledProxyConstructString()); + JSHandle method = JSObject::GetMethod(thread, handler, key); + + // 6.ReturnIfAbrupt(trap). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7.If trap is undefined, then + // a.Assert: target has a [[Construct]] internal method. + // b.Return Construct(target, argumentsList, newTarget). + if (method->IsUndefined()) { + ASSERT(target->IsConstructor()); + return JSFunction::Construct(thread, target, argv, newTarget); + } + + // 8.Let argArray be CreateArrayFromList(argumentsList). + JSHandle arrHandle = JSArray::CreateArrayFromList(thread, argv); + + // step 8 ~ 9 Call(trap, handler, «target, argArray, newTarget »). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle nextArgv(factory->NewTaggedArray(3)); // 3: «target, argArray, newTarget » + nextArgv->Set(thread, 0, target.GetTaggedValue()); + nextArgv->Set(thread, 1, arrHandle.GetTaggedValue()); + nextArgv->Set(thread, 2, newTarget.GetTaggedValue()); // 2: the third value is newTarget + JSTaggedValue newObj = JSFunction::Call(thread, method, handler, nextArgv); + // 10.ReturnIfAbrupt(newObj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11.If Type(newObj) is not Object, throw a TypeError exception. + if (!newObj.IsObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new object is not object", JSTaggedValue::Exception()); + } + // 12.Return newObj. + return newObj; +} + +bool JSProxy::IsArray(JSThread *thread) const +{ + if (GetHandler().IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", false); + } + return GetTarget().IsArray(thread); +} + +JSHandle JSProxy::GetSourceTarget(JSThread *thread) const +{ + JSMutableHandle proxy(thread, JSTaggedValue(this)); + JSMutableHandle target(thread, proxy->GetTarget()); + while (target->IsJSProxy()) { + proxy.Update(target.GetTaggedValue()); + target.Update(proxy->GetTarget()); + } + return target; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_proxy.h b/ecmascript/js_proxy.h new file mode 100644 index 0000000000000000000000000000000000000000..9af426977c3ba9ecc6b040ebf047f86e0c6f8f6d --- /dev/null +++ b/ecmascript/js_proxy.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSPROXY_H +#define PANDA_RUNTIME_ECMASCRIPT_JSPROXY_H + +#include "ecmascript/tagged_array.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSProxy final : public ECMAObject { +public: + static JSProxy *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static const JSProxy *ConstCast(const TaggedObject *object) + { + return static_cast(object); + } + + // ES6 9.5.15 ProxyCreate(target, handler) + static JSHandle ProxyCreate(JSThread *thread, const JSHandle &target, + const JSHandle &handler); + // ES6 9.5.1 [[GetPrototypeOf]] ( ) + static JSTaggedValue GetPrototype(JSThread *thread, const JSHandle &proxy); + // ES6 9.5.2 [[SetPrototypeOf]] (V) + static bool SetPrototype(JSThread *thread, const JSHandle &proxy, const JSHandle &proto); + // ES6 9.5.3 [[IsExtensible]] ( ) + static bool IsExtensible(JSThread *thread, const JSHandle &proxy); + // ES6 9.5.4 [[PreventExtensions]] ( ) + static bool PreventExtensions(JSThread *thread, const JSHandle &proxy); + // ES6 9.5.5 [[GetOwnProperty]] (P) + static bool GetOwnProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + PropertyDescriptor &desc); + // ES6 9.5.6 [[DefineOwnProperty]] (P, Desc) + static bool DefineOwnProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + const PropertyDescriptor &desc); + // ES6 9.5.7 [[HasProperty]] (P) + static bool HasProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key); + // ES6 9.5.8 [[Get]] (P, Receiver) + static inline OperationResult GetProperty(JSThread *thread, const JSHandle &proxy, + const JSHandle &key) + { + return GetProperty(thread, proxy, key, JSHandle::Cast(proxy)); + } + static OperationResult GetProperty(JSThread *thread, const JSHandle &proxy, + const JSHandle &key, const JSHandle &receiver); + // ES6 9.5.9 [[Set]] ( P, V, Receiver) + static inline bool SetProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + const JSHandle &value, bool mayThrow = false) + { + return SetProperty(thread, proxy, key, value, JSHandle::Cast(proxy), mayThrow); + } + static bool SetProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver, + bool mayThrow = false); + // ES6 9.5.10 [[Delete]] (P) + static bool DeleteProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key); + + // ES6 9.5.12 [[OwnPropertyKeys]] () + static JSHandle OwnPropertyKeys(JSThread *thread, const JSHandle &proxy); + + void SetCallable(bool callable) const + { + GetClass()->SetCallable(callable); + } + + void SetConstructor(bool constructor) const + { + GetClass()->SetConstructor(constructor); + } + + void SetCallTarget([[maybe_unused]] const JSThread *thread, JSMethod *p) + { + SetMethod(p); + } + + JSHandle GetSourceTarget(JSThread *thread) const; + + // ES6 9.5.13 [[Call]] (thisArgument, argumentsList) + static JSTaggedValue CallInternal(JSThread *thread, const JSHandle &proxy, + const JSHandle &thisArg, const JSHandle &argv); + // ES6 9.5.14 [[Construct]] ( argumentsList, newTarget) + static JSTaggedValue ConstructInternal(JSThread *thread, const JSHandle &proxy, + const JSHandle &argv, const JSHandle &newTarget); + + static constexpr size_t METHOD_OFFSET = ECMAObject::SIZE; + SET_GET_NATIVE_FIELD(Method, JSMethod, METHOD_OFFSET, TARGET_OFFSET) + ACCESSORS(Target, TARGET_OFFSET, HANDLER_OFFSET) + + ACCESSORS(Handler, HANDLER_OFFSET, SIZE) + bool IsArray(JSThread *thread) const; + DECL_DUMP() + + DECL_VISIT_OBJECT(TARGET_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSPROXY_H diff --git a/ecmascript/js_realm.h b/ecmascript/js_realm.h new file mode 100644 index 0000000000000000000000000000000000000000..5672be0e7d94cb356176c323c034050789c55415 --- /dev/null +++ b/ecmascript/js_realm.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSREALM_H +#define PANDA_RUNTIME_ECMASCRIPT_JSREALM_H + +#include "js_global_object.h" + +namespace panda::ecmascript { +class JSRealm : public JSObject { +public: + static JSRealm *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static constexpr size_t VALUE_OFFSET = ObjectHeaderSize(); + ACCESSORS(Value, VALUE_OFFSET, GLOBAL_ENV_OFFSET) + ACCESSORS(GlobalEnv, GLOBAL_ENV_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSREALM_H \ No newline at end of file diff --git a/ecmascript/js_regexp.h b/ecmascript/js_regexp.h new file mode 100644 index 0000000000000000000000000000000000000000..5bf65408d0807f23f480d0777f7cbc4cd3f79a5c --- /dev/null +++ b/ecmascript/js_regexp.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSREGEXP_H +#define PANDA_RUNTIME_ECMASCRIPT_JSREGEXP_H + +#include "ecmascript/js_object.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/ecma_macros.h" + +namespace panda::ecmascript { +class JSRegExp : public JSObject { +public: + static JSRegExp *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSRegExp()); + return static_cast(object); + } + + static constexpr size_t LAST_INDEX_OFFSET = JSObject::SIZE; + ACCESSORS(LastIndex, LAST_INDEX_OFFSET, REGEXP_BYTE_CODE_OFFSET); + ACCESSORS(ByteCodeBuffer, REGEXP_BYTE_CODE_OFFSET, ORIGINAL_SOURCE_OFFSET) + ACCESSORS(OriginalSource, ORIGINAL_SOURCE_OFFSET, ORIGINAL_FLAGS_OFFSET) + ACCESSORS(OriginalFlags, ORIGINAL_FLAGS_OFFSET, LENGTH_OFFSET) + ACCESSORS(Length, LENGTH_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LAST_INDEX_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSPRIMITIVEREF_H diff --git a/ecmascript/js_serializer.cpp b/ecmascript/js_serializer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1a3e31829e315c844716aa3a16c2dcf1335e0b6d --- /dev/null +++ b/ecmascript/js_serializer.cpp @@ -0,0 +1,1670 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_serializer.h" + +#include +#include + +#include "ecmascript/base/array_helper.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_float32_array.h" +#include "ecmascript/js_float64_array.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_int16_array.h" +#include "ecmascript/js_int32_array.h" +#include "ecmascript/js_int8_array.h" +#include "ecmascript/js_regexp.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_uint16_array.h" +#include "ecmascript/js_uint32_array.h" +#include "ecmascript/js_uint8_array.h" +#include "ecmascript/js_uint8_clamped_array.h" +#include "ecmascript/linked_hash_table-inl.h" +#include "libpandabase/mem/mem.h" +#include "securec.h" + +namespace panda::ecmascript { +constexpr size_t INITIAL_CAPACITY = 64; +constexpr int CAPACITY_INCREASE_RATE = 2; + +bool JSSerializer::WriteType(SerializationUID id) +{ + uint8_t rawId = static_cast(id); + return WriteRawData(&rawId, sizeof(rawId)); +} + +// Write JSTaggedValue could be either a pointer to a HeapObject or a value +bool JSSerializer::SerializeJSTaggedValue(const JSHandle &value) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + if (!value->IsHeapObject()) { + if (!WritePrimitiveValue(value)) { + return false; + } + } else { + if (!WriteTaggedObject(value)) { + return false; + } + } + return true; +} + +// Write JSTaggedValue that is pure value +bool JSSerializer::WritePrimitiveValue(const JSHandle &value) +{ + if (value->IsNull()) { + return WriteType(SerializationUID::JS_NULL); + } + if (value->IsUndefined()) { + return WriteType(SerializationUID::JS_UNDEFINED); + } + if (value->IsTrue()) { + return WriteType(SerializationUID::JS_TRUE); + } + if (value->IsFalse()) { + return WriteType(SerializationUID::JS_FALSE); + } + if (value->IsInt()) { + return WriteInt(value->GetInt()); + } + if (value->IsDouble()) { + return WriteDouble(value->GetDouble()); + } + if (value->IsHole()) { + return WriteType(SerializationUID::HOLE); + } + return false; +} + +bool JSSerializer::WriteInt(int32_t value) +{ + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::INT32)) { + return false; + } + if (!WriteRawData(&value, sizeof(value))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteInt(uint32_t value) +{ + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::UINT32)) { + return false; + } + if (!WriteRawData(&value, sizeof(value))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteDouble(double value) +{ + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::DOUBLE)) { + return false; + } + if (!WriteRawData(&value, sizeof(value))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteBoolean(bool value) +{ + if (value) { + return WriteType(SerializationUID::C_TRUE); + } + return WriteType(SerializationUID::C_FALSE); +} + +// Write length for marking how many bytes should be read +bool JSSerializer::WriteLength(uint32_t length) +{ + return WriteRawData(&length, sizeof(length)); +} + +bool JSSerializer::WriteRawData(const void *data, size_t length) +{ + if (length <= 0) { + return false; + } + if ((bufferSize_ + length) > bufferCapacity_) { + if (!AllocateBuffer(length)) { + return false; + } + } + errno_t rc; + rc = memcpy_s(buffer_ + bufferSize_, bufferCapacity_ - bufferSize_, data, length); + if (rc != EOK) { + LOG(ERROR, RUNTIME) << "Failed to memcpy_s Data"; + return false; + } + bufferSize_ += length; + return true; +} + +bool JSSerializer::AllocateBuffer(size_t bytes) +{ + size_t oldSize = bufferSize_; + size_t newSize = oldSize + bytes; + if (bufferCapacity_ == 0) { + if (bytes < INITIAL_CAPACITY) { + buffer_ = reinterpret_cast(malloc(INITIAL_CAPACITY)); + if (buffer_ != nullptr) { + bufferCapacity_ = INITIAL_CAPACITY; + return true; + } else { + return false; + } + } else { + buffer_ = reinterpret_cast(malloc(bytes)); + if (buffer_ != nullptr) { + bufferCapacity_ = bytes; + return true; + } else { + return false; + } + } + } + if (newSize > bufferCapacity_) { + if (!ExpandBuffer(newSize)) { + return false; + } + } + return true; +} + +bool JSSerializer::ExpandBuffer(size_t requestedSize) +{ + size_t newCapacity = bufferCapacity_ * CAPACITY_INCREASE_RATE; + uint8_t *newBuffer = reinterpret_cast(malloc(newCapacity)); + if (newBuffer == nullptr) { + return false; + } + errno_t rc; + rc = memcpy_s(newBuffer, newCapacity, buffer_, bufferSize_); + if (rc != EOK) { + LOG(ERROR, RUNTIME) << "Failed to memcpy_s Data"; + free(newBuffer); + return false; + } + free(buffer_); + buffer_ = newBuffer; + bufferCapacity_ = newCapacity; + return true; +} + +// Transfer ownership of buffer, should not use this Serializer after release +std::pair JSSerializer::ReleaseBuffer() +{ + auto res = std::make_pair(buffer_, bufferSize_); + buffer_ = nullptr; + bufferSize_ = 0; + bufferCapacity_ = 0; + objectId_ = 0; + return res; +} + +bool JSSerializer::IsSerialized(uintptr_t addr) const +{ + if (referenceMap_.find(addr) != referenceMap_.end()) { + return true; + } + return false; +} + +bool JSSerializer::WriteIfSerialized(uintptr_t addr) +{ + size_t oldSize = bufferSize_; + auto iter = referenceMap_.find(addr); + if (iter == referenceMap_.end()) { + return false; + } + uint64_t id = iter->second; + if (!WriteType(SerializationUID::TAGGED_OBJECT_REFERNCE)) { + return false; + } + if (!WriteRawData(&id, sizeof(uint64_t))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +// Write HeapObject +bool JSSerializer::WriteTaggedObject(const JSHandle &objValue) +{ + JSHandle obj = JSHandle::Cast(objValue); + uintptr_t addr = reinterpret_cast(*obj); + bool serialized = IsSerialized(addr); + if (serialized) { + return WriteIfSerialized(addr); + } + referenceMap_.insert(std::pair(addr, objectId_)); + objectId_++; + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_ERROR: + case JSType::JS_EVAL_ERROR: + case JSType::JS_RANGE_ERROR: + case JSType::JS_REFERENCE_ERROR: + case JSType::JS_TYPE_ERROR: + case JSType::JS_URI_ERROR: + case JSType::JS_SYNTAX_ERROR: + return WriteJSError(obj); + case JSType::JS_DATE: + return WriteJSDate(obj); + case JSType::JS_ARRAY: + return WriteJSArray(obj); + case JSType::JS_MAP: + return WriteJSMap(obj); + case JSType::JS_SET: + return WriteJSSet(obj); + case JSType::JS_REG_EXP: + return WriteJSRegExp(obj); + case JSType::JS_INT8_ARRAY: + return WriteJSTypedArray(obj, SerializationUID::JS_INT8_ARRAY); + case JSType::JS_UINT8_ARRAY: + return WriteJSTypedArray(obj, SerializationUID::JS_UINT8_ARRAY); + case JSType::JS_UINT8_CLAMPED_ARRAY: + return WriteJSTypedArray(obj, SerializationUID::JS_UINT8_CLAMPED_ARRAY); + case JSType::JS_INT16_ARRAY: + return WriteJSTypedArray(obj, SerializationUID::JS_INT16_ARRAY); + case JSType::JS_UINT16_ARRAY: + return WriteJSTypedArray(obj, SerializationUID::JS_UINT16_ARRAY); + case JSType::JS_INT32_ARRAY: + return WriteJSTypedArray(obj, SerializationUID::JS_INT32_ARRAY); + case JSType::JS_UINT32_ARRAY: + return WriteJSTypedArray(obj, SerializationUID::JS_UINT32_ARRAY); + case JSType::JS_FLOAT32_ARRAY: + return WriteJSTypedArray(obj, SerializationUID::JS_FLOAT32_ARRAY); + case JSType::JS_FLOAT64_ARRAY: + return WriteJSTypedArray(obj, SerializationUID::JS_FLOAT64_ARRAY); + case JSType::JS_ARRAY_BUFFER: + return WriteJSArrayBuffer(objValue); + case JSType::STRING: + return WriteEcmaString(objValue); + case JSType::JS_OBJECT: + return WritePlainObject(obj); + default: + break; + } + return false; +} + +bool JSSerializer::WriteJSError(const JSHandle &obj) +{ + size_t oldSize = bufferSize_; + JSType errorType = obj->GetJSHClass()->GetObjectType(); + if (!WriteJSErrorHeader(errorType)) { + return false; + } + auto globalConst = thread_->GlobalConstants(); + JSHandle handleMsg = globalConst->GetHandledMessageString(); + JSHandle msg = + JSObject::GetProperty(thread_, JSHandle::Cast(obj), handleMsg).GetValue(); + // Write error message + if (!SerializeJSTaggedValue(msg)) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteJSErrorHeader(JSType type) +{ + switch (type) { + case JSType::JS_ERROR: + return WriteType(SerializationUID::JS_ERROR); + case JSType::JS_EVAL_ERROR: + return WriteType(SerializationUID::EVAL_ERROR); + case JSType::JS_RANGE_ERROR: + return WriteType(SerializationUID::RANGE_ERROR); + case JSType::JS_REFERENCE_ERROR: + return WriteType(SerializationUID::REFERENCE_ERROR); + case JSType::JS_TYPE_ERROR: + return WriteType(SerializationUID::TYPE_ERROR); + case JSType::JS_URI_ERROR: + return WriteType(SerializationUID::URI_ERROR); + case JSType::JS_SYNTAX_ERROR: + return WriteType(SerializationUID::SYNTAX_ERROR); + default: + UNREACHABLE(); + } + return false; +} + +bool JSSerializer::WriteJSDate(const JSHandle &obj) +{ + JSHandle date = JSHandle::Cast(obj); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::JS_DATE)) { + return false; + } + if (!WritePlainObject(JSHandle::Cast(date))) { + bufferSize_ = oldSize; + return false; + } + double timeValue = date->GetTimeValue().GetDouble(); + if (!WriteDouble(timeValue)) { + bufferSize_ = oldSize; + return false; + } + double localOffset = date->GetLocalOffset().GetDouble(); + if (!WriteDouble(localOffset)) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteJSArray(const JSHandle &obj) +{ + JSHandle array = JSHandle::Cast(obj); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::JS_ARRAY)) { + return false; + } + if (!WritePlainObject(JSHandle::Cast(array))) { + bufferSize_ = oldSize; + return false; + } + uint32_t arrayLength = static_cast(array->GetLength().GetInt()); + if (!WriteInt(arrayLength)) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteEcmaString(const JSHandle &value) +{ + JSHandle string = JSHandle::Cast(value); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::ECMASTRING)) { + return false; + } + size_t length = string->GetLength(); + if (!WriteLength(static_cast(length))) { + bufferSize_ = oldSize; + return false; + } + const uint8_t *data = string->GetDataUtf8(); + const uint8_t strEnd = '\0'; + if (!WriteRawData(data, length) || !WriteRawData(&strEnd, sizeof(uint8_t))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteJSMap(const JSHandle &obj) +{ + JSHandle map = JSHandle::Cast(obj); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::JS_MAP)) { + return false; + } + if (!WritePlainObject(JSHandle::Cast(map))) { + bufferSize_ = oldSize; + return false; + } + int size = map->GetSize(); + if (!WriteLength(static_cast(size))) { + bufferSize_ = oldSize; + return false; + } + for (int i = 0; i < size; i++) { + JSHandle key(thread_, map->GetKey(i)); + if (!SerializeJSTaggedValue(key)) { + bufferSize_ = oldSize; + return false; + } + JSHandle value(thread_, map->GetValue(i)); + if (!SerializeJSTaggedValue(value)) { + bufferSize_ = oldSize; + return false; + } + } + return true; +} + +bool JSSerializer::WriteJSSet(const JSHandle &obj) +{ + JSHandle set = JSHandle::Cast(obj); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::JS_SET)) { + return false; + } + if (!WritePlainObject(JSHandle::Cast(set))) { + bufferSize_ = oldSize; + return false; + } + int size = set->GetSize(); + if (!WriteLength(static_cast(size))) { + bufferSize_ = oldSize; + return false; + } + for (int i = 0; i < size; i++) { + JSHandle value(thread_, set->GetValue(i)); + if (!SerializeJSTaggedValue(value)) { + bufferSize_ = oldSize; + return false; + } + } + return true; +} + +bool JSSerializer::WriteJSRegExp(const JSHandle &obj) +{ + JSHandle regExp = JSHandle::Cast(obj); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::JS_REG_EXP)) { + return false; + } + if (!WritePlainObject(obj)) { + bufferSize_ = oldSize; + return false; + } + uint32_t bufferSize = static_cast(regExp->GetLength().GetInt()); + if (!WriteLength(bufferSize)) { + bufferSize_ = oldSize; + return false; + } + // Write Accessor(ByteCodeBuffer) which is a pointer to a Dynbuffer + JSHandle bufferValue(thread_, regExp->GetByteCodeBuffer()); + JSHandle np = JSHandle::Cast(bufferValue); + void *dynBuffer = np->GetExternalPointer(); + if (!WriteRawData(dynBuffer, bufferSize)) { + bufferSize_ = oldSize; + return false; + } + // Write Accessor(OriginalSource) + JSHandle originalSource(thread_, regExp->GetOriginalSource()); + if (!SerializeJSTaggedValue(originalSource)) { + bufferSize_ = oldSize; + return false; + } + // Write Accessor(OriginalFlags) + JSHandle originalFlags(thread_, regExp->GetOriginalFlags()); + if (!SerializeJSTaggedValue(originalFlags)) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteJSTypedArray(const JSHandle &obj, SerializationUID uId) +{ + JSHandle typedArray = JSHandle::Cast(obj); + size_t oldSize = bufferSize_; + if (!WriteType(uId)) { + return false; + } + if (!WritePlainObject(obj)) { + bufferSize_ = oldSize; + return false; + } + // Write ACCESSORS(ViewedArrayBuffer) which is a pointer to an ArrayBuffer + JSHandle viewedArrayBuffer(thread_, typedArray->GetViewedArrayBuffer()); + if (!WriteJSArrayBuffer(viewedArrayBuffer)) { + bufferSize_ = oldSize; + return false; + } + // Write ACCESSORS(TypedArrayName) + JSHandle typedArrayName(thread_, typedArray->GetTypedArrayName()); + if (!SerializeJSTaggedValue(typedArrayName)) { + bufferSize_ = oldSize; + return false; + } + // Write ACCESSORS(ByteLength) + JSTaggedValue byteLength = typedArray->GetByteLength(); + if (!WriteRawData(&byteLength, sizeof(JSTaggedValue))) { + bufferSize_ = oldSize; + return false; + } + // Write ACCESSORS(ByteOffset) + JSTaggedValue byteOffset = typedArray->GetByteOffset(); + if (!WriteRawData(&byteOffset, sizeof(JSTaggedValue))) { + bufferSize_ = oldSize; + return false; + } + // Write ACCESSORS(ArrayLength) + JSTaggedValue arrayLength = typedArray->GetArrayLength(); + if (!WriteRawData(&arrayLength, sizeof(JSTaggedValue))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteNativeFunctionPointer(const JSHandle &value) +{ + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::NATIVE_FUNCTION_POINTER)) { + return false; + } + JSTaggedValue pointer = value.GetTaggedValue(); + if (!WriteRawData(&pointer, sizeof(JSTaggedValue))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteJSArrayBuffer(const JSHandle &value) +{ + size_t oldSize = bufferSize_; + JSHandle arrayBuffer = JSHandle::Cast(value); + + if (arrayBuffer->IsDetach()) { + return false; + } + + if (!WriteType(SerializationUID::JS_ARRAY_BUFFER)) { + return false; + } + + // Write Accessors(ArrayBufferByteLength) + JSTaggedValue taggedLength = arrayBuffer->GetArrayBufferByteLength(); + if (!WriteRawData(&taggedLength, sizeof(JSTaggedValue))) { + bufferSize_ = oldSize; + return false; + } + + // write Accessor shared which indicate the C memeory is shared + bool shared = arrayBuffer->GetShared().ToBoolean(); + if (!WriteBoolean(shared)) { + bufferSize_ = oldSize; + return false; + } + + if (shared) { + JSHandle np(thread_, arrayBuffer->GetArrayBufferData()); + void *buffer = np->GetExternalPointer(); + uint64_t bufferAddr = (uint64_t)buffer; + if (!WriteRawData(&bufferAddr, sizeof(uint64_t))) { + bufferSize_ = oldSize; + return false; + } + } else { + uint32_t byteLength = JSTaggedNumber(taggedLength).ToUint32(); + // Write Accessors(ArrayBufferData) which is a pointer to a DynBuffer + JSHandle np(thread_, arrayBuffer->GetArrayBufferData()); + void *buffer = np->GetExternalPointer(); + if (!WriteRawData(buffer, byteLength)) { + bufferSize_ = oldSize; + return false; + } + } + + // write obj properties + if (!WritePlainObject(JSHandle::Cast(value))) { + bufferSize_ = oldSize; + return false; + } + + return true; +} + +bool JSSerializer::WritePlainObject(const JSHandle &obj) +{ + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::JS_PLAIN_OBJECT)) { + return false; + } + // Get the number of elements stored in obj + uint32_t elementsLength = obj->GetNumberOfElements(); + if (!WriteLength(elementsLength)) { + bufferSize_ = oldSize; + return false; + } + std::vector keyVector; + JSObject::GetALLElementKeysIntoVector(thread_, obj, keyVector); + // Write elements' description attributes and value + if (keyVector.size() != elementsLength) { + bufferSize_ = oldSize; + return false; + } + for (uint32_t i = 0; i < elementsLength; i++) { + JSHandle key(thread_, keyVector[i]); + if (!SerializeJSTaggedValue(key)) { + bufferSize_ = oldSize; + return false; + } + PropertyDescriptor desc(thread_); + JSObject::OrdinaryGetOwnProperty(thread_, obj, key, desc); + if (!WriteDesc(desc)) { + bufferSize_ = oldSize; + return false; + } + JSHandle value = desc.GetValue(); + if (!SerializeJSTaggedValue(value)) { + bufferSize_ = oldSize; + return false; + } + } + // Get the number of k-v form properties stored in obj + keyVector.clear(); + uint32_t propertiesLength = obj->GetNumberOfKeys(); + if (!WriteLength(propertiesLength)) { + bufferSize_ = oldSize; + return false; + } + JSObject::GetAllKeys(thread_, obj, keyVector); + if (keyVector.size() != propertiesLength) { + bufferSize_ = oldSize; + return false; + } + // Write keys' description attributes and related values + for (uint32_t i = 0; i < propertiesLength; i++) { + if (keyVector.empty()) { + bufferSize_ = oldSize; + return false; + } + JSHandle key(thread_, keyVector[i]); + if (!SerializeJSTaggedValue(key)) { + bufferSize_ = oldSize; + return false; + } + PropertyDescriptor desc(thread_); + JSObject::OrdinaryGetOwnProperty(thread_, obj, key, desc); + if (!WriteDesc(desc)) { + bufferSize_ = oldSize; + return false; + } + JSHandle value = desc.GetValue(); + if (!SerializeJSTaggedValue(value)) { + bufferSize_ = oldSize; + return false; + } + } + return true; +} + +bool JSSerializer::WriteDesc(const PropertyDescriptor &desc) +{ + size_t oldSize = bufferSize_; + bool isWritable = desc.IsWritable(); + if (!WriteBoolean(isWritable)) { + bufferSize_ = oldSize; + return false; + } + bool isEnumerable = desc.IsEnumerable(); + if (!WriteBoolean(isEnumerable)) { + bufferSize_ = oldSize; + return false; + } + bool isConfigurable = desc.IsConfigurable(); + if (!WriteBoolean(isConfigurable)) { + bufferSize_ = oldSize; + return false; + } + bool hasWritable = desc.HasWritable(); + if (!WriteBoolean(hasWritable)) { + bufferSize_ = oldSize; + return false; + } + bool hasEnumerable = desc.HasEnumerable(); + if (!WriteBoolean(hasEnumerable)) { + bufferSize_ = oldSize; + return false; + } + bool hasConfigurable = desc.HasConfigurable(); + if (!WriteBoolean(hasConfigurable)) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +SerializationUID JSDeserializer::ReadType() +{ + SerializationUID uid; + if (position_ >= end_) { + return SerializationUID::UNKNOWN; + } + uid = static_cast(*position_); + if (uid < SerializationUID::JS_NULL || uid > SerializationUID::NATIVE_FUNCTION_POINTER) { + return SerializationUID::UNKNOWN; + } + position_++; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return uid; +} + +bool JSDeserializer::ReadInt(int32_t *value) +{ + size_t len = sizeof(int32_t); + if (len > static_cast(end_ - position_)) { + return false; + } + if (memcpy_s(value, len, position_, len) != EOK) { + UNREACHABLE(); + } + position_ += len; + return true; +} + +bool JSDeserializer::ReadInt(uint32_t *value) +{ + size_t len = sizeof(uint32_t); + if (len > static_cast(end_ - position_)) { + return false; + } + if (memcpy_s(value, len, position_, len) != EOK) { + UNREACHABLE(); + } + position_ += len; + return true; +} + +bool JSDeserializer::ReadObjectId(uint64_t *objectId) +{ + size_t len = sizeof(uint64_t); + if (len > static_cast(end_ - position_)) { + return false; + } + if (memcpy_s(objectId, len, position_, len) != EOK) { + UNREACHABLE(); + } + position_ += len; + return true; +} + +bool JSDeserializer::ReadDouble(double *value) +{ + size_t len = sizeof(double); + if (len > static_cast(end_ - position_)) { + return false; + } + if (memcpy_s(value, len, position_, len) != EOK) { + UNREACHABLE(); + } + position_ += len; + return true; +} + +JSDeserializer::~JSDeserializer() +{ + free(begin_); + begin_ = nullptr; +} + +JSHandle JSDeserializer::DeserializeJSTaggedValue() +{ + SerializationUID uid = ReadType(); + if (uid == SerializationUID::UNKNOWN) { + return JSHandle(); + } + switch (uid) { + case SerializationUID::JS_NULL: + return JSHandle(thread_, JSTaggedValue::Null()); + case SerializationUID::JS_UNDEFINED: + return JSHandle(thread_, JSTaggedValue::Undefined()); + case SerializationUID::JS_TRUE: + return JSHandle(thread_, JSTaggedValue::True()); + case SerializationUID::JS_FALSE: + return JSHandle(thread_, JSTaggedValue::False()); + case SerializationUID::HOLE: + return JSHandle(thread_, JSTaggedValue::Hole()); + case SerializationUID::INT32: { + int32_t value; + if (!ReadInt(&value)) { + return JSHandle(); + } + return JSHandle(thread_, JSTaggedValue(value)); + } + case SerializationUID::UINT32: { + uint32_t value; + if (!ReadInt(&value)) { + return JSHandle(); + } + return JSHandle(thread_, JSTaggedValue(value)); + } + case SerializationUID::DOUBLE: { + double value; + if (!ReadDouble(&value)) { + return JSHandle(); + } + return JSHandle(thread_, JSTaggedValue(value)); + } + case SerializationUID::JS_ERROR: + case SerializationUID::EVAL_ERROR: + case SerializationUID::RANGE_ERROR: + case SerializationUID::REFERENCE_ERROR: + case SerializationUID::TYPE_ERROR: + case SerializationUID::URI_ERROR: + case SerializationUID::SYNTAX_ERROR: + return ReadJSError(uid); + case SerializationUID::JS_DATE: + return ReadJSDate(); + case SerializationUID::JS_PLAIN_OBJECT: + return ReadPlainObject(); + case SerializationUID::JS_ARRAY: + return ReadJSArray(); + case SerializationUID::ECMASTRING: + return ReadEcmaString(); + case SerializationUID::JS_MAP: + return ReadJSMap(); + case SerializationUID::JS_SET: + return ReadJSSet(); + case SerializationUID::JS_REG_EXP: + return ReadJSRegExp(); + case SerializationUID::JS_INT8_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_INT8_ARRAY); + case SerializationUID::JS_UINT8_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_UINT8_ARRAY); + case SerializationUID::JS_UINT8_CLAMPED_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_UINT8_CLAMPED_ARRAY); + case SerializationUID::JS_INT16_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_INT16_ARRAY); + case SerializationUID::JS_UINT16_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_UINT16_ARRAY); + case SerializationUID::JS_INT32_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_INT32_ARRAY); + case SerializationUID::JS_UINT32_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_UINT32_ARRAY); + case SerializationUID::JS_FLOAT32_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_FLOAT32_ARRAY); + case SerializationUID::JS_FLOAT64_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_FLOAT64_ARRAY); + case SerializationUID::NATIVE_FUNCTION_POINTER: + return ReadNativeFunctionPointer(); + case SerializationUID::JS_ARRAY_BUFFER: + return ReadJSArrayBuffer(); + case SerializationUID::TAGGED_OBJECT_REFERNCE: + return ReadReference(); + default: + return JSHandle(); + } +} + +JSHandle JSDeserializer::ReadJSError(SerializationUID uid) +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + base::ErrorType errorType; + switch (uid) { + case SerializationUID::JS_ERROR: + errorType = base::ErrorType::ERROR; + break; + case SerializationUID::EVAL_ERROR: + errorType = base::ErrorType::EVAL_ERROR; + break; + case SerializationUID::RANGE_ERROR: + errorType = base::ErrorType::RANGE_ERROR; + break; + case SerializationUID::REFERENCE_ERROR: + errorType = base::ErrorType::REFERENCE_ERROR; + break; + case SerializationUID::TYPE_ERROR: + errorType = base::ErrorType::TYPE_ERROR; + break; + case SerializationUID::URI_ERROR: + errorType = base::ErrorType::URI_ERROR; + break; + case SerializationUID::SYNTAX_ERROR: + errorType = base::ErrorType::URI_ERROR; + break; + default: + UNREACHABLE(); + } + JSHandle msg = DeserializeJSTaggedValue(); + JSHandle handleMsg(msg); + JSHandle errorTag = JSHandle::Cast(factory->NewJSError(errorType, handleMsg)); + referenceMap_.insert(std::pair(objectId_++, errorTag)); + return errorTag; +} + +JSHandle JSDeserializer::ReadJSDate() +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle dateFunction = env->GetDateFunction(); + JSHandle date = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(dateFunction), dateFunction)); + JSHandle dateTag = JSHandle::Cast(date); + referenceMap_.insert(std::pair(objectId_++, dateTag)); + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(dateTag)) { + return JSHandle(); + } + double timeValue; + if (!JudgeType(SerializationUID::DOUBLE) || !ReadDouble(&timeValue)) { + return JSHandle(); + } + date->SetTimeValue(thread_, JSTaggedValue(timeValue)); + double localOffset; + if (!JudgeType(SerializationUID::DOUBLE) || !ReadDouble(&localOffset)) { + return JSHandle(); + } + date->SetLocalOffset(thread_, JSTaggedValue(localOffset)); + return dateTag; +} + +JSHandle JSDeserializer::ReadJSArray() +{ + JSHandle jsArray = thread_->GetEcmaVM()->GetFactory()->NewJSArray(); + JSHandle arrayTag = JSHandle::Cast(jsArray); + referenceMap_.insert(std::pair(objectId_++, arrayTag)); + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(arrayTag)) { + return JSHandle(); + } + uint32_t arrLength; + if (!JudgeType(SerializationUID::UINT32) || !ReadInt(&arrLength)) { + return JSHandle(); + } + jsArray->SetLength(thread_, JSTaggedValue(arrLength)); + return arrayTag; +} + +JSHandle JSDeserializer::ReadEcmaString() +{ + uint32_t stringLength; + if (!ReadInt(&stringLength)) { + return JSHandle(); + } + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + uint8_t *string = reinterpret_cast(GetBuffer(stringLength + 1)); + if (string == nullptr) { + return JSHandle(); + } + + JSHandle ecmaString = factory->NewFromUtf8(string, stringLength); + JSHandle stringTag = JSHandle(ecmaString); + referenceMap_.insert(std::pair(objectId_++, stringTag)); + return stringTag; +} + +JSHandle JSDeserializer::ReadPlainObject() +{ + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle objFunc(thread_, env->GetObjectFunction().GetObject()); + JSHandle jsObject = + thread_->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + JSHandle objTag = JSHandle::Cast(jsObject); + referenceMap_.insert(std::pair(objectId_++, objTag)); + if (!DefinePropertiesAndElements(objTag)) { + return JSHandle(); + } + return objTag; +} + +JSHandle JSDeserializer::ReadJSMap() +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle mapFunction = env->GetBuiltinsMapFunction(); + JSHandle jsMap = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(mapFunction), mapFunction)); + JSHandle mapTag = JSHandle::Cast(jsMap); + referenceMap_.insert(std::pair(objectId_++, mapTag)); + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(mapTag)) { + return JSHandle(); + } + uint32_t size; + if (!ReadInt(&size)) { + return JSHandle(); + } + JSTaggedValue linkedMap = LinkedHashMap::Create(thread_); + jsMap->SetLinkedMap(thread_, linkedMap); + for (uint32_t i = 0; i < size; i++) { + JSHandle key = DeserializeJSTaggedValue(); + if (key.IsEmpty()) { + return JSHandle(); + } + JSHandle value = DeserializeJSTaggedValue(); + if (value.IsEmpty()) { + return JSHandle(); + } + JSMap::Set(thread_, jsMap, key, value); + } + return mapTag; +} + +JSHandle JSDeserializer::ReadJSSet() +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle setFunction = env->GetBuiltinsSetFunction(); + JSHandle jsSet = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(setFunction), setFunction)); + JSHandle setTag = JSHandle::Cast(jsSet); + referenceMap_.insert(std::pair(objectId_++, setTag)); + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(setTag)) { + return JSHandle(); + } + uint32_t size; + if (!ReadInt(&size)) { + return JSHandle(); + } + JSTaggedValue linkedSet = LinkedHashSet::Create(thread_); + jsSet->SetLinkedSet(thread_, linkedSet); + for (uint32_t i = 0; i < size; i++) { + JSHandle key = DeserializeJSTaggedValue(); + if (key.IsEmpty()) { + return JSHandle(); + } + JSSet::Add(thread_, jsSet, key); + } + return setTag; +} + +JSHandle JSDeserializer::ReadJSRegExp() +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle regexpFunction = env->GetRegExpFunction(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(regexpFunction), regexpFunction); + JSHandle regExp = JSHandle::Cast(obj); + JSHandle regexpTag = JSHandle::Cast(regExp); + referenceMap_.insert(std::pair(objectId_++, regexpTag)); + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(regexpTag)) { + return JSHandle(); + } + uint32_t bufferSize; + if (!ReadInt(&bufferSize)) { + return JSHandle(); + } + void *buffer = GetBuffer(bufferSize); + if (buffer == nullptr) { + return JSHandle(); + } + factory->NewJSRegExpByteCodeData(regExp, buffer, bufferSize); + JSHandle originalSource = DeserializeJSTaggedValue(); + regExp->SetOriginalSource(thread_, originalSource); + JSHandle originalFlags = DeserializeJSTaggedValue(); + regExp->SetOriginalFlags(thread_, originalFlags); + return regexpTag; +} + +JSHandle JSDeserializer::ReadJSTypedArray(SerializationUID uid) +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle target; + JSHandle obj; + JSHandle objTag; + switch (uid) { + case SerializationUID::JS_INT8_ARRAY: { + target = env->GetInt8ArrayFunction(); + JSHandle int8Array = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + obj = JSHandle::Cast(int8Array); + objTag = JSHandle::Cast(obj); + break; + } + case SerializationUID::JS_UINT8_ARRAY: { + target = env->GetUint8ArrayFunction(); + JSHandle uint8Array = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + obj = JSHandle::Cast(uint8Array); + objTag = JSHandle::Cast(obj); + break; + } + case SerializationUID::JS_UINT8_CLAMPED_ARRAY: { + target = env->GetUint8ClampedArrayFunction(); + JSHandle constructor = factory->NewJSObjectByConstructor(JSHandle(target), target); + JSHandle uint8ClampedArray = JSHandle::Cast(constructor); + obj = JSHandle::Cast(uint8ClampedArray); + objTag = JSHandle::Cast(obj); + break; + } + case SerializationUID::JS_INT16_ARRAY: { + target = env->GetInt16ArrayFunction(); + JSHandle int16Array = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + obj = JSHandle::Cast(int16Array); + objTag = JSHandle::Cast(obj); + break; + } + case SerializationUID::JS_UINT16_ARRAY: { + target = env->GetUint16ArrayFunction(); + JSHandle uint16Array = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + obj = JSHandle::Cast(uint16Array); + objTag = JSHandle::Cast(obj); + break; + } + case SerializationUID::JS_INT32_ARRAY: { + target = env->GetInt32ArrayFunction(); + JSHandle int32Array = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + obj = JSHandle::Cast(int32Array); + objTag = JSHandle::Cast(obj); + break; + } + case SerializationUID::JS_UINT32_ARRAY: { + target = env->GetUint32ArrayFunction(); + JSHandle uint32Array = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + obj = JSHandle::Cast(uint32Array); + objTag = JSHandle::Cast(obj); + break; + } + case SerializationUID::JS_FLOAT32_ARRAY: { + target = env->GetFloat32ArrayFunction(); + JSHandle float32Array = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + obj = JSHandle::Cast(float32Array); + objTag = JSHandle::Cast(obj); + referenceMap_.insert(std::pair(objectId_++, objTag)); + break; + } + case SerializationUID::JS_FLOAT64_ARRAY: { + target = env->GetFloat32ArrayFunction(); + JSHandle float64Array = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + obj = JSHandle::Cast(float64Array); + objTag = JSHandle::Cast(obj); + referenceMap_.insert(std::pair(objectId_++, objTag)); + break; + } + default: + UNREACHABLE(); + } + referenceMap_.insert(std::pair(objectId_++, objTag)); + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(objTag)) { + return JSHandle(); + } + + JSHandle viewedArrayBuffer = DeserializeJSTaggedValue(); + if (viewedArrayBuffer.IsEmpty()) { + return JSHandle(); + } + SetViewedArrayBuffer(obj, viewedArrayBuffer.GetTaggedValue()); + + JSHandle typedArrayName = DeserializeJSTaggedValue(); + if (typedArrayName.IsEmpty()) { + return JSHandle(); + } + SetTypedArrayName(obj, typedArrayName); + + JSTaggedValue byteLength; + if (!ReadJSTaggedValue(&byteLength) || !byteLength.IsNumber()) { + return JSHandle(); + } + SetByteLength(obj, JSTaggedValue::ToInt32(thread_, JSHandle(thread_, byteLength))); + + JSTaggedValue byteOffset; + if (!ReadJSTaggedValue(&byteOffset) || !byteOffset.IsNumber()) { + return JSHandle(); + } + SetByteOffset(obj, JSTaggedValue::ToInt32(thread_, JSHandle(thread_, byteOffset))); + + JSTaggedValue arrayLength; + if (!ReadJSTaggedValue(&arrayLength) || !byteOffset.IsNumber()) { + return JSHandle(); + } + SetArrayLength(obj, JSTaggedValue::ToInt32(thread_, JSHandle(thread_, arrayLength))); + return objTag; +} + +JSHandle JSDeserializer::ReadNativeFunctionPointer() +{ + JSTaggedValue pointer; + if (!ReadJSTaggedValue(&pointer)) { + return JSHandle(); + } + return JSHandle(thread_, pointer); +} + +JSHandle JSDeserializer::ReadJSArrayBuffer() +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + // read access length + JSTaggedValue taggedLength; + if (!ReadJSTaggedValue(&taggedLength)) { + return JSHandle(); + } + // read access shared + bool shared = false; + if (!ReadBoolean(&shared)) { + return JSHandle(); + } + // create jsarraybuffer + JSHandle arrayBufferTag; + uint32_t byteLength = JSTaggedNumber(taggedLength).ToUint32(); + if (shared) { + uint64_t *bufferAddr = (uint64_t*)GetBuffer(sizeof(uint64_t)); + void* bufferData = ToVoidPtr(*bufferAddr); + JSHandle arrayBuffer = factory->NewJSArrayBuffer(bufferData, byteLength, nullptr, nullptr); + arrayBufferTag = JSHandle::Cast(arrayBuffer); + referenceMap_.insert(std::pair(objectId_++, arrayBufferTag)); + } else { + void *fromBuffer = GetBuffer(byteLength); + if (fromBuffer == nullptr) { + return arrayBufferTag; + } + JSHandle arrayBuffer = factory->NewJSArrayBuffer(byteLength); + arrayBufferTag = JSHandle::Cast(arrayBuffer); + referenceMap_.insert(std::pair(objectId_++, arrayBufferTag)); + JSHandle np(thread_, arrayBuffer->GetArrayBufferData()); + void *toBuffer = np->GetExternalPointer(); + if (memcpy_s(toBuffer, byteLength, fromBuffer, byteLength) != EOK) { + UNREACHABLE(); + } + } + // read jsarraybuffer properties + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(arrayBufferTag)) { + return JSHandle(); + } + + return arrayBufferTag; +} + +bool JSDeserializer::ReadJSTaggedValue(JSTaggedValue *value) +{ + size_t len = sizeof(JSTaggedValue); + if (len > static_cast(end_ - position_)) { + return false; + } + if (memcpy_s(value, len, position_, len) != EOK) { + UNREACHABLE(); + } + position_ += len; + return true; +} + +void *JSDeserializer::GetBuffer(uint32_t bufferSize) +{ + const uint8_t *buffer = nullptr; + if (bufferSize > static_cast(end_ - position_)) { + return nullptr; + } + buffer = position_; + position_ += bufferSize; + uint8_t *retBuffer = const_cast(buffer); + return static_cast(retBuffer); +} + +JSHandle JSDeserializer::ReadReference() +{ + uint64_t objId; + if (!ReadObjectId(&objId)) { + return JSHandle(); + } + auto objIter = referenceMap_.find(objId); + if (objIter == referenceMap_.end()) { + return JSHandle(); + } + return objIter->second; +} + +bool JSDeserializer::JudgeType(SerializationUID targetUid) +{ + if (ReadType() != targetUid) { + return false; + } + return true; +} + +bool JSDeserializer::DefinePropertiesAndElements(const JSHandle &obj) +{ + uint32_t elementLength; + if (!ReadInt(&elementLength)) { + return false; + } + for (uint32_t i = 0; i < elementLength; i++) { + JSHandle key = DeserializeJSTaggedValue(); + if (key.IsEmpty()) { + return false; + } + PropertyDescriptor desc(thread_); + if (!ReadDesc(&desc)) { + return false; + } + JSHandle value = DeserializeJSTaggedValue(); + if (value.IsEmpty()) { + return false; + } + desc.SetValue(value); + if (!JSTaggedValue::DefineOwnProperty(thread_, obj, key, desc)) { + return false; + } + } + + uint32_t propertyLength; + if (!ReadInt(&propertyLength)) { + return false; + } + for (uint32_t i = 0; i < propertyLength; i++) { + JSHandle key = DeserializeJSTaggedValue(); + if (key.IsEmpty()) { + return false; + } + PropertyDescriptor desc(thread_); + if (!ReadDesc(&desc)) { + return false; + } + JSHandle value = DeserializeJSTaggedValue(); + if (value.IsEmpty()) { + return false; + } + desc.SetValue(value); + if (!JSTaggedValue::DefineOwnProperty(thread_, obj, key, desc)) { + return false; + } + } + return true; +} + +bool JSDeserializer::ReadDesc(PropertyDescriptor *desc) +{ + bool isWritable = false; + if (!ReadBoolean(&isWritable)) { + return false; + } + bool isEnumerable = false; + if (!ReadBoolean(&isEnumerable)) { + return false; + } + bool isConfigurable = false; + if (!ReadBoolean(&isConfigurable)) { + return false; + } + bool hasWritable = false; + if (!ReadBoolean(&hasWritable)) { + return false; + } + bool hasEnumerable = false; + if (!ReadBoolean(&hasEnumerable)) { + return false; + } + bool hasConfigurable = false; + if (!ReadBoolean(&hasConfigurable)) { + return false; + } + if (hasWritable) { + desc->SetWritable(isWritable); + } + if (hasEnumerable) { + desc->SetEnumerable(isEnumerable); + } + if (hasConfigurable) { + desc->SetConfigurable(isConfigurable); + } + return true; +} + +bool JSDeserializer::ReadBoolean(bool *value) +{ + SerializationUID uid = ReadType(); + if (uid == SerializationUID::C_TRUE) { + *value = true; + return true; + } + if (uid == SerializationUID::C_FALSE) { + *value = false; + return true; + } + return false; +} + +void JSDeserializer::SetViewedArrayBuffer(const JSHandle &obj, JSTaggedValue viewedArrayBuffer) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(*obj)->SetViewedArrayBuffer(thread_, viewedArrayBuffer); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(*obj)->SetViewedArrayBuffer(thread_, viewedArrayBuffer); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(*obj)->SetViewedArrayBuffer(thread_, viewedArrayBuffer); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(*obj)->SetViewedArrayBuffer(thread_, viewedArrayBuffer); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(*obj)->SetViewedArrayBuffer(thread_, viewedArrayBuffer); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(*obj)->SetViewedArrayBuffer(thread_, viewedArrayBuffer); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(*obj)->SetViewedArrayBuffer(thread_, viewedArrayBuffer); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(*obj)->SetViewedArrayBuffer(thread_, viewedArrayBuffer); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(*obj)->SetViewedArrayBuffer(thread_, viewedArrayBuffer); + break; + default: + break; + } +} + +void JSDeserializer::SetTypedArrayName(const JSHandle &obj, const JSHandle &typedArrayName) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(*obj)->SetTypedArrayName(thread_, typedArrayName); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(*obj)->SetTypedArrayName(thread_, typedArrayName); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(*obj)->SetTypedArrayName(thread_, typedArrayName); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(*obj)->SetTypedArrayName(thread_, typedArrayName); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(*obj)->SetTypedArrayName(thread_, typedArrayName); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(*obj)->SetTypedArrayName(thread_, typedArrayName); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(*obj)->SetTypedArrayName(thread_, typedArrayName); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(*obj)->SetTypedArrayName(thread_, typedArrayName); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(*obj)->SetTypedArrayName(thread_, typedArrayName); + break; + default: + break; + } +} + +void JSDeserializer::SetByteLength(const JSHandle &obj, int32_t byteLength) +{ + auto byteLengthValue = JSTaggedValue(byteLength); + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(*obj)->SetByteLength(thread_, byteLengthValue); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(*obj)->SetByteLength(thread_, byteLengthValue); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(*obj)->SetByteLength(thread_, byteLengthValue); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(*obj)->SetByteLength(thread_, byteLengthValue); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(*obj)->SetByteLength(thread_, byteLengthValue); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(*obj)->SetByteLength(thread_, byteLengthValue); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(*obj)->SetByteLength(thread_, byteLengthValue); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(*obj)->SetByteLength(thread_, byteLengthValue); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(*obj)->SetByteLength(thread_, byteLengthValue); + break; + default: + break; + } +} + +void JSDeserializer::SetByteOffset(const JSHandle &obj, int32_t byteOffset) +{ + auto byteOffsetValue = JSTaggedValue(byteOffset); + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(*obj)->SetByteOffset(thread_, byteOffsetValue); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(*obj)->SetByteOffset(thread_, byteOffsetValue); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(*obj)->SetByteOffset(thread_, byteOffsetValue); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(*obj)->SetByteOffset(thread_, byteOffsetValue); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(*obj)->SetByteOffset(thread_, byteOffsetValue); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(*obj)->SetByteOffset(thread_, byteOffsetValue); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(*obj)->SetByteOffset(thread_, byteOffsetValue); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(*obj)->SetByteOffset(thread_, byteOffsetValue); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(*obj)->SetByteOffset(thread_, byteOffsetValue); + break; + default: + break; + } +} + +void JSDeserializer::SetArrayLength(const JSHandle &obj, int32_t arrayLength) +{ + auto arrayLengthValue = JSTaggedValue(arrayLength); + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(*obj)->SetArrayLength(thread_, arrayLengthValue); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(*obj)->SetArrayLength(thread_, arrayLengthValue); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(*obj)->SetArrayLength(thread_, arrayLengthValue); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(*obj)->SetArrayLength(thread_, arrayLengthValue); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(*obj)->SetArrayLength(thread_, arrayLengthValue); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(*obj)->SetArrayLength(thread_, arrayLengthValue); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(*obj)->SetArrayLength(thread_, arrayLengthValue); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(*obj)->SetArrayLength(thread_, arrayLengthValue); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(*obj)->SetArrayLength(thread_, arrayLengthValue); + break; + default: + break; + } +} + +bool Serializer::WriteValue( + JSThread *thread, const JSHandle &value, const JSHandle &transfer) +{ + if (data_ != nullptr) { + return false; + } + data_.reset(new SerializationData); + if (!PrepareTransfer(thread, transfer)) { + return false; + } + if (!valueSerializer_.SerializeJSTaggedValue(value)) { + return false; + } + if (!FinalizeTransfer(thread, transfer)) { + return false; + } + std::pair pair = valueSerializer_.ReleaseBuffer(); + data_->value_.reset(pair.first); + data_->dataSize_ = pair.second; + return true; +} + +std::unique_ptr Serializer::Release() +{ + return std::move(data_); +} + +bool Serializer::PrepareTransfer(JSThread *thread, const JSHandle &transfer) +{ + if (transfer->IsUndefined()) { + return true; + } + if (!transfer->IsJSArray()) { + return false; + } + int len = base::ArrayHelper::GetArrayLength(thread, transfer); + int k = 0; + while (k < len) { + bool exists = JSTaggedValue::HasProperty(thread, transfer, k); + if (exists) { + JSHandle element = JSArray::FastGetPropertyByValue(thread, transfer, k); + if (!element->IsArrayBuffer()) { + return false; + } + arrayBufferIdxs_.emplace_back(k); + } + k++; + } + return true; +} + +bool Serializer::FinalizeTransfer(JSThread *thread, const JSHandle &transfer) +{ + for (int idx : arrayBufferIdxs_) { + JSHandle element = JSArray::FastGetPropertyByValue(thread, transfer, idx); + JSArrayBuffer::Cast(element->GetHeapObject())->Detach(thread); + } + return true; +} + +JSHandle Deserializer::ReadValue() +{ + return valueDeserializer_.DeserializeJSTaggedValue(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_serializer.h b/ecmascript/js_serializer.h new file mode 100644 index 0000000000000000000000000000000000000000..70a5a792c7813b49e2b4563cd95c7d33d4957e91 --- /dev/null +++ b/ecmascript/js_serializer.h @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_SERIALIZER_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_SERIALIZER_H + +#include + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_date.h" +#include "ecmascript/js_map.h" +#include "ecmascript/js_native_pointer.h" +#include "ecmascript/js_object.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/js_typed_array.h" +#include "ecmascript/regexp/dyn_chunk.h" + +namespace panda::ecmascript { +enum class SerializationUID : uint8_t { + // JS special values + JS_NULL = 0x01, + JS_UNDEFINED, + JS_TRUE, + JS_FALSE, + HOLE, + // Number types + INT32, + UINT32, + DOUBLE, + // Not support yet, BigInt type has not been implemented in ark engine + BIGINT, + ECMASTRING, + // Boolean types + C_TRUE, + C_FALSE, + // Tagged object reference mark + TAGGED_OBJECT_REFERNCE, + // Support tagged objct id reference begin + JS_DATE, + JS_REG_EXP, + JS_PLAIN_OBJECT, + JS_SET, + JS_MAP, + JS_ARRAY, + JS_ARRAY_BUFFER, + // TypedArray begin + JS_UINT8_ARRAY, + JS_UINT8_CLAMPED_ARRAY, + JS_UINT16_ARRAY, + JS_UINT32_ARRAY, + JS_INT8_ARRAY, + JS_INT16_ARRAY, + JS_INT32_ARRAY, + JS_FLOAT32_ARRAY, + JS_FLOAT64_ARRAY, + // TypedArray end + // Support tagged objct id reference end + // Error UIDs + JS_ERROR, + EVAL_ERROR, + RANGE_ERROR, + REFERENCE_ERROR, + TYPE_ERROR, + URI_ERROR, + SYNTAX_ERROR, + ERROR_MESSAGE_BEGIN, + ERROR_MESSAGE_END, + // NativeFunctionPointer + NATIVE_FUNCTION_POINTER, + UNKNOWN +}; + +class JSSerializer { +public: + explicit JSSerializer(JSThread *thread) : thread_(thread) {} + ~JSSerializer() = default; + bool SerializeJSTaggedValue(const JSHandle &value); + + // Return pointer to the buffer and its length, should not use this Serializer anymore after Release + std::pair ReleaseBuffer(); + +private: + bool WriteTaggedObject(const JSHandle &obj); + bool WritePrimitiveValue(const JSHandle &value); + bool WriteInt(int32_t value); + bool WriteInt(uint32_t value); + bool WriteDouble(double value); + bool WriteRawData(const void *data, size_t length); + bool WriteType(SerializationUID uId); + bool AllocateBuffer(size_t bytes); + bool ExpandBuffer(size_t requestedSize); + bool WriteBoolean(bool value); + bool WriteJSError(const JSHandle &obj); + bool WriteJSErrorHeader(JSType type); + bool WriteJSDate(const JSHandle &obj); + bool WriteJSArray(const JSHandle &obj); + bool WriteJSMap(const JSHandle &obj); + bool WriteJSSet(const JSHandle &obj); + bool WriteJSRegExp(const JSHandle &obj); + bool WriteEcmaString(const JSHandle &value); + bool WriteJSTypedArray(const JSHandle &obj, SerializationUID uId); + bool WritePlainObject(const JSHandle &obj); + bool WriteLength(uint32_t length); + bool WriteNativeFunctionPointer(const JSHandle &value); + bool WriteJSArrayBuffer(const JSHandle &value); + bool WriteDesc(const PropertyDescriptor &desc); + bool IsSerialized(uintptr_t addr) const; + bool WriteIfSerialized(uintptr_t addr); + + NO_MOVE_SEMANTIC(JSSerializer); + NO_COPY_SEMANTIC(JSSerializer); + + JSThread *thread_; + uint8_t *buffer_ = nullptr; + size_t bufferSize_ = 0; + size_t bufferCapacity_ = 0; + // The Reference map is used for check whether a tagged object has been serialized + // Reference map works only if no gc happens during serialization + std::map referenceMap_; + uint64_t objectId_ = 0; +}; + +class JSDeserializer { +public: + // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + JSDeserializer(JSThread *thread, uint8_t *data, size_t size) + : thread_(thread), begin_(data), position_(data), end_(data + size) + { + } + ~JSDeserializer(); + JSHandle DeserializeJSTaggedValue(); + +private: + bool ReadInt(int32_t *value); + bool ReadInt(uint32_t *value); + bool ReadObjectId(uint64_t *objectId); + bool ReadDouble(double *value); + SerializationUID ReadType(); + JSHandle ReadJSError(SerializationUID uid); + JSHandle ReadJSDate(); + JSHandle ReadJSArray(); + JSHandle ReadPlainObject(); + JSHandle ReadEcmaString(); + JSHandle ReadJSMap(); + JSHandle ReadJSSet(); + JSHandle ReadJSRegExp(); + JSHandle ReadJSTypedArray(SerializationUID uid); + JSHandle ReadNativeFunctionPointer(); + JSHandle ReadJSArrayBuffer(); + JSHandle ReadReference(); + bool JudgeType(SerializationUID targetUid); + void *GetBuffer(uint32_t bufferSize); + bool ReadJSTaggedValue(JSTaggedValue *originalFlags); + bool DefinePropertiesAndElements(const JSHandle &obj); + bool ReadDesc(PropertyDescriptor *desc); + bool ReadBoolean(bool *value); + void SetViewedArrayBuffer(const JSHandle &obj, JSTaggedValue viewedArrayBuffer); + void SetTypedArrayName(const JSHandle &obj, const JSHandle &typedArrayName); + void SetByteLength(const JSHandle &obj, int32_t byteLength); + void SetByteOffset(const JSHandle &obj, int32_t byteOffset); + void SetArrayLength(const JSHandle &obj, int32_t arrayLength); + + NO_MOVE_SEMANTIC(JSDeserializer); + NO_COPY_SEMANTIC(JSDeserializer); + + JSThread *thread_ = nullptr; + uint8_t *begin_ = nullptr; + const uint8_t *position_ = nullptr; + const uint8_t * const end_ = nullptr; + uint64_t objectId_ = 0; + std::map> referenceMap_; +}; + +class SerializationData { +public: + SerializationData() : dataSize_(0), value_(nullptr) {} + ~SerializationData() = default; + + uint8_t* GetData() const + { + return value_.get(); + } + size_t GetSize() const + { + return dataSize_; + } + +private: + struct Deleter { + void operator()(uint8_t* ptr) const + { + free(ptr); + } + }; + + size_t dataSize_; + std::unique_ptr value_; + +private: + friend class Serializer; + + NO_COPY_SEMANTIC(SerializationData); +}; + +class Serializer { +public: + explicit Serializer(JSThread *thread) : valueSerializer_(thread) {} + ~Serializer() = default; + + bool WriteValue(JSThread *thread, const JSHandle &value, const JSHandle &transfer); + std::unique_ptr Release(); + +private: + bool PrepareTransfer(JSThread *thread, const JSHandle &transfer); + bool FinalizeTransfer(JSThread *thread, const JSHandle &transfer); + +private: + ecmascript::JSSerializer valueSerializer_; + std::unique_ptr data_; + CVector arrayBufferIdxs_; + + NO_COPY_SEMANTIC(Serializer); +}; + +class Deserializer { +public: + explicit Deserializer(JSThread *thread, SerializationData* data) + : valueDeserializer_(thread, data->GetData(), data->GetSize()) {} + ~Deserializer() = default; + + JSHandle ReadValue(); + +private: + ecmascript::JSDeserializer valueDeserializer_; + + NO_COPY_SEMANTIC(Deserializer); +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_SERIALIZER_H diff --git a/ecmascript/js_set.cpp b/ecmascript/js_set.cpp new file mode 100644 index 0000000000000000000000000000000000000000..58a4694ffd6942bfa340fb70c9c865bf678110e9 --- /dev/null +++ b/ecmascript/js_set.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_set.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "linked_hash_table-inl.h" +#include "utils/bit_utils.h" + +namespace panda::ecmascript { +void JSSet::Add(JSThread *thread, const JSHandle &set, const JSHandle &value) +{ + if (!LinkedHashSet::IsKey(value.GetTaggedValue())) { + // throw error + THROW_TYPE_ERROR(thread, "the value must be Key of JSSet"); + } + JSHandle setHandle(thread, LinkedHashSet::Cast(set->GetLinkedSet().GetTaggedObject())); + + auto table = LinkedHashSet::Add(thread, setHandle, value); + set->SetLinkedSet(thread, table); +} + +bool JSSet::Delete(const JSThread *thread, const JSHandle &set, const JSHandle &value) +{ + JSHandle setHandle(thread, LinkedHashSet::Cast(set->GetLinkedSet().GetTaggedObject())); + int entry = setHandle->FindElement(value.GetTaggedValue()); + if (entry == -1) { + return false; + } + setHandle->RemoveEntry(thread, entry); + auto table = LinkedHashSet::Shrink(thread, setHandle); + set->SetLinkedSet(thread, table); + return true; +} + +void JSSet::Clear(const JSThread *thread, const JSHandle &set) +{ + LinkedHashSet *linkedSet = LinkedHashSet::Cast(set->GetLinkedSet().GetTaggedObject()); + linkedSet->Clear(thread); +} + +bool JSSet::Has(JSTaggedValue value) const +{ + return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->Has(value); +} + +int JSSet::GetSize() const +{ + return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->NumberOfElements(); +} + +JSTaggedValue JSSet::GetValue(int entry) const +{ + ASSERT_PRINT(entry >= 0 && entry < GetSize(), "entry must be non-negative integer less than capacity"); + return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->GetValue(entry); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_set.h b/ecmascript/js_set.h new file mode 100644 index 0000000000000000000000000000000000000000..81d4a5f028ceea5add9e3d374c94437a836aa0d2 --- /dev/null +++ b/ecmascript/js_set.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSSET_H +#define PANDA_RUNTIME_ECMASCRIPT_JSSET_H + +#include +#include "js_object.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +class JSSet : public JSObject { +public: + static JSSet *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static bool Delete(const JSThread *thread, const JSHandle &set, const JSHandle &value); + + static void Add(JSThread *thread, const JSHandle &set, const JSHandle &value); + + static void Clear(const JSThread *thread, const JSHandle &set); + + bool Has(JSTaggedValue value) const; + + int GetSize() const; + + JSTaggedValue GetValue(int entry) const; + + static constexpr size_t LINKED_SET_OFFSET = JSObject::SIZE; + ACCESSORS(LinkedSet, LINKED_SET_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LINKED_SET_OFFSET, SIZE) + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JSSET_H diff --git a/ecmascript/js_set_iterator.cpp b/ecmascript/js_set_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c1530c6b12c10708102ec4a25ff05ef94a62704e --- /dev/null +++ b/ecmascript/js_set_iterator.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/builtins/builtins_errors.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_set.h" +#include "ecmascript/linked_hash_table-inl.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +JSTaggedValue JSSetIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.If Type(O) is not Object, throw a TypeError exception. + JSHandle input(BuiltinsBase::GetThis(argv)); + + // 3.If O does not have all of the internal slots of a Set Iterator Instance (23.2.5.3), throw a TypeError + // exception. + if (!input->IsJSSetIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not a set iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + iter->Update(thread); + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + // 4.Let s be O.[[IteratedSet]]. + JSHandle iteratedSet(thread, iter->GetIteratedSet()); + + // 5.Let index be O.[[SetNextIndex]]. + int index = iter->GetNextIndex().GetInt(); + IterationKind itemKind = IterationKind(iter->GetIterationKind().GetInt()); + // 7.If s is undefined, return CreateIterResultObject(undefined, true). + if (iteratedSet->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + JSHandle set(iteratedSet); + int totalElements = set->NumberOfElements() + set->NumberOfDeletedElements(); + + while (index < totalElements) { + if (!set->GetKey(index).IsHole()) { + iter->SetNextIndex(thread, JSTaggedValue(index + 1)); + JSHandle key(thread, set->GetKey(index)); + // If itemKind is value + if (itemKind == IterationKind::VALUE || itemKind == IterationKind::KEY) { + return JSIterator::CreateIterResultObject(thread, key, false).GetTaggedValue(); + } + // If itemKind is key+value, then + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle array(factory->NewTaggedArray(2)); // 2: key and value pair + array->Set(thread, 0, key); + array->Set(thread, 1, key); + JSHandle keyAndValue(JSArray::CreateArrayFromList(thread, array)); + return JSIterator::CreateIterResultObject(thread, keyAndValue, false).GetTaggedValue(); + } + index++; + } + // 13.Set O.[[IteratedSet]] to undefined. + iter->SetIteratedSet(thread, JSTaggedValue::Undefined()); + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); +} + +void JSSetIterator::Update(const JSThread *thread) +{ + [[maybe_unused]] DisallowGarbageCollection noGc; + JSTaggedValue iteratedSet = GetIteratedSet(); + if (iteratedSet.IsUndefined()) { + return; + } + LinkedHashSet *set = LinkedHashSet::Cast(iteratedSet.GetTaggedObject()); + if (set->GetNextTable().IsHole()) { + return; + } + int index = GetNextIndex().GetInt(); + JSTaggedValue nextTable = set->GetNextTable(); + while (!nextTable.IsHole()) { + index -= set->GetDeletedElementsAt(index); + set = LinkedHashSet::Cast(nextTable.GetTaggedObject()); + nextTable = set->GetNextTable(); + } + SetIteratedSet(thread, JSTaggedValue(set)); + SetNextIndex(thread, JSTaggedValue(index)); +} + +JSHandle JSSetIterator::CreateSetIterator(JSThread *thread, const JSHandle &obj, + IterationKind kind) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (!obj->IsJSSet()) { + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", undefinedHandle); + } + JSHandle iter(factory->NewJSSetIterator(JSHandle(obj), kind)); + return iter; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_set_iterator.h b/ecmascript/js_set_iterator.h new file mode 100644 index 0000000000000000000000000000000000000000..aca57c689dddfc8aa1e9d04495a4e6731307046e --- /dev/null +++ b/ecmascript/js_set_iterator.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_SET_ITERATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_SET_ITERATOR_H + +#include "js_object.h" +#include "js_iterator.h" + +namespace panda::ecmascript { +class JSSetIterator : public JSObject { +public: + static JSSetIterator *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsJSSetIterator()); + return static_cast(obj); + } + + static JSHandle CreateSetIterator(JSThread *thread, const JSHandle &obj, + IterationKind kind); + + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + + void Update(const JSThread *thread); + + static constexpr size_t ITERATED_SET_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedSet, ITERATED_SET_OFFSET, NEXT_INDEX_OFFSET); + ACCESSORS(NextIndex, NEXT_INDEX_OFFSET, ITERATION_KIND_OFFSET); + ACCESSORS(IterationKind, ITERATION_KIND_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_SET_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_SET_ITERATOR_H diff --git a/ecmascript/js_stable_array.cpp b/ecmascript/js_stable_array.cpp new file mode 100644 index 0000000000000000000000000000000000000000..56999a5d0e517705affda4a729a5a7c08d655428 --- /dev/null +++ b/ecmascript/js_stable_array.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_stable_array.h" +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_invoker.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array.h" +#include "interpreter/fast_runtime_stub-inl.h" + +namespace panda::ecmascript { +JSTaggedValue JSStableArray::Push(JSHandle receiver, EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + array_size_t argc = argv->GetArgsNumber(); + uint32_t oldLength = receiver->GetArrayLength(); + array_size_t newLength = argc + oldLength; + + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + if (newLength > elements->GetLength()) { + elements = *JSObject::GrowElementsCapacity(thread, JSHandle::Cast(receiver), newLength); + } + + for (array_size_t k = 0; k < argc; k++) { + JSHandle value = argv->GetCallArg(k); + elements->Set(thread, oldLength + k, value.GetTaggedValue()); + } + receiver->SetArrayLength(thread, newLength); + + return JSTaggedValue(newLength); +} + +JSTaggedValue JSStableArray::Pop(JSHandle receiver, EcmaRuntimeCallInfo *argv) +{ + DISALLOW_GARBAGE_COLLECTION; + JSThread *thread = argv->GetThread(); + uint32_t length = receiver->GetArrayLength(); + if (length == 0) { + return JSTaggedValue::Undefined(); + } + + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + array_size_t capacity = elements->GetLength(); + array_size_t index = length - 1; + auto result = elements->Get(index); + if (JSObject::ShouldTransToDict(capacity, index)) { + elements->Trim(thread, length); + } else { + elements->Set(thread, index, JSTaggedValue::Hole()); + } + receiver->SetArrayLength(thread, index); + return result; +} + +JSTaggedValue JSStableArray::Join(JSHandle receiver, EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + uint32_t length = receiver->GetArrayLength(); + JSHandle sepHandle = base::BuiltinsBase::GetCallArg(argv, 0); + int sep = ','; + uint32_t sepLength = 1; + JSHandle sepStringHandle; + if (!sepHandle->IsUndefined()) { + if (sepHandle->IsString()) { + sepStringHandle = JSHandle::Cast(sepHandle); + } else { + sepStringHandle = JSTaggedValue::ToString(thread, sepHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + if (sepStringHandle->IsUtf8() && sepStringHandle->GetLength() == 1) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + sep = sepStringHandle->GetDataUtf8()[0]; + } else if (sepStringHandle->GetLength() == 0) { + sep = JSStableArray::SeparatorFlag::MINUS_TWO; + sepLength = 0; + } else { + sep = JSStableArray::SeparatorFlag::MINUS_ONE; + sepLength = sepStringHandle->GetLength(); + } + } + if (length == 0) { + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + return globalConst->GetEmptyString(); + } + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + size_t allocateLength = 0; + bool isOneByte = (sep != JSStableArray::SeparatorFlag::MINUS_ONE) || sepStringHandle->IsUtf8(); + CVector> vec; + JSMutableHandle elementHandle(thread, JSTaggedValue::Undefined()); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + for (array_size_t k = 0; k < length; k++) { + JSTaggedValue element = elements->Get(k); + if (!element.IsUndefinedOrNull() && !element.IsHole()) { + if (!element.IsString()) { + elementHandle.Update(element); + JSHandle strElement = JSTaggedValue::ToString(thread, elementHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + element = strElement.GetTaggedValue(); + elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + } + auto nextStr = EcmaString::Cast(element.GetTaggedObject()); + JSHandle nextStrHandle(thread, nextStr); + vec.push_back(nextStrHandle); + isOneByte = nextStr->IsUtf8() ? isOneByte : false; + allocateLength += nextStr->GetLength(); + } else { + vec.push_back(JSHandle(globalConst->GetHandledEmptyString())); + } + } + allocateLength += sepLength * (length - 1); + auto newString = EcmaString::AllocStringObject(allocateLength, isOneByte, thread->GetEcmaVM()); + int current = 0; + DISALLOW_GARBAGE_COLLECTION; + for (array_size_t k = 0; k < length; k++) { + if (k > 0) { + if (sep >= 0) { + newString->WriteData(static_cast(sep), current); + } else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) { + newString->WriteData(*sepStringHandle, current, allocateLength - current, sepLength); + } + current += sepLength; + } + JSHandle nextStr = vec[k]; + int nextLength = nextStr->GetLength(); + newString->WriteData(*nextStr, current, allocateLength - current, nextLength); + current += nextLength; + } + return JSTaggedValue(newString); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_stable_array.h b/ecmascript/js_stable_array.h new file mode 100644 index 0000000000000000000000000000000000000000..17e4a35931d883301446553ced7ed4b5e3f8e929 --- /dev/null +++ b/ecmascript/js_stable_array.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_STABLE_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_STABLE_ARRAY_H + +#include +#include "ecmascript/js_array.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class JSStableArray { +public: + enum SeparatorFlag : int { MINUS_ONE = -1, MINUS_TWO = -2 }; + static constexpr int FLAG_OF_SEPARATOR = 3; + static JSTaggedValue Push(JSHandle receiver, EcmaRuntimeCallInfo *argv); + static JSTaggedValue Pop(JSHandle receiver, EcmaRuntimeCallInfo *argv); + static JSTaggedValue Join(JSHandle receiver, EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_STABLE_ARRAY_H \ No newline at end of file diff --git a/ecmascript/js_string_iterator.cpp b/ecmascript/js_string_iterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba383067e5b185d7d498bc93d6e1ba47b647451b --- /dev/null +++ b/ecmascript/js_string_iterator.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_string_iterator.h" + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +JSHandle JSStringIterator::CreateStringIterator(const JSThread *thread, + const JSHandle &string) +{ + // 1. Assert: Type(string) is String. + // 2. Let iterator be ObjectCreate(%StringIteratorPrototype%, [[IteratedString]], [[StringIteratorNextIndex]] ?.) + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle strIterCtor = env->GetStringIterator(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle iterator = JSHandle::Cast( + factory->NewJSObjectByConstructor(JSHandle(strIterCtor), strIterCtor)); + // 3. Set iterator’s [[IteratedString]] internal slot to string. + // 4. Set iterator’s [[StringIteratorNextIndex]] internal slot to 0. + iterator->SetIteratedString(thread, string); + iterator->SetStringIteratorNextIndex(thread, JSTaggedValue(0)); + return iterator; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_string_iterator.h b/ecmascript/js_string_iterator.h new file mode 100644 index 0000000000000000000000000000000000000000..7a56db52a1f906a5c4149a16421696b1f37eb59d --- /dev/null +++ b/ecmascript/js_string_iterator.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_STRING_ITERATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_STRING_ITERATOR_H + +#include "ecmascript/ecma_string.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" + +namespace panda { +namespace ecmascript { +class JSStringIterator : public JSObject { +public: + static JSStringIterator *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsStringIterator()); + return static_cast(object); + } + + static JSHandle CreateStringIterator(const JSThread *thread, const JSHandle &string); + + static constexpr size_t ITERATED_STRING_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedString, ITERATED_STRING_OFFSET, STRING_ITERATOR_NEXT_INDEX_OFFSET) + ACCESSORS(StringIteratorNextIndex, STRING_ITERATOR_NEXT_INDEX_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_STRING_OFFSET, SIZE) +}; +} // namespace ecmascript +} // namespace panda +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_STRING_ITERATOR_H diff --git a/ecmascript/js_symbol.h b/ecmascript/js_symbol.h new file mode 100644 index 0000000000000000000000000000000000000000..c9370567d1a3ef7f09c15534e8f23b5a4eb1b3a3 --- /dev/null +++ b/ecmascript/js_symbol.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JSSYMBOL_H +#define PANDA_RUNTIME_ECMASCRIPT_JSSYMBOL_H + +#include "ecmascript/ecma_string.h" +#include "ecmascript/js_object.h" + +namespace panda { +namespace ecmascript { +class JSSymbol : public TaggedObject { +public: + static constexpr uint64_t IS_PRIVATE = 1U << 0U; + static constexpr uint64_t IS_WELL_KNOWN_SYMBOL = 1U << 1U; + static constexpr uint64_t IS_IN_PUBLIC_SYMBOL_TABLE = 1U << 2U; + static constexpr uint64_t IS_INTERESTING_SYMBOL = 1U << 3U; + static constexpr uint64_t IS_PRIVATE_NAME = 1U << 4U; + static constexpr uint64_t IS_PRIVATE_BRAND = 1U << 5U; + + static constexpr int SYMBOL_HAS_INSTANCE_TYPE = 0; + static constexpr int SYMBOL_TO_PRIMITIVE_TYPE = 1; + static constexpr int SYMBOL_DEFAULT_TYPE = 2; + + static constexpr const uint32_t LINEAR_X = 1103515245U; + static constexpr const uint32_t LINEAR_Y = 12345U; + static constexpr const uint32_t LINEAR_SEED = 987654321U; + +public: + static JSSymbol *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsSymbol()); + return static_cast(object); + } + + static inline uint32_t ComputeHash() + { + uint32_t hashSeed = LINEAR_SEED + std::time(nullptr); + uint32_t hash = hashSeed * LINEAR_X + LINEAR_Y; + return hash; + } + + bool IsPrivate() const + { + JSTaggedValue flags = this->GetFlags(); + ASSERT(flags.IsInteger()); + return (flags.GetRawData() & IS_PRIVATE) != 0U; + } + + void SetPrivate(const JSThread *thread) + { + JSTaggedValue flags = this->GetFlags(); + if (flags.IsUndefined()) { + SetFlags(thread, JSTaggedValue(IS_PRIVATE)); + return; + } + SetFlags(thread, JSTaggedValue(flags.GetRawData() | IS_PRIVATE)); + } + + bool IsWellKnownSymbol() const + { + JSTaggedValue flags = this->GetFlags(); + ASSERT(flags.IsInteger()); + return (flags.GetRawData() & IS_WELL_KNOWN_SYMBOL) != 0U; + } + + void SetWellKnownSymbol(const JSThread *thread) + { + JSTaggedValue flags = this->GetFlags(); + if (flags.IsUndefined()) { + SetFlags(thread, JSTaggedValue(static_cast(IS_WELL_KNOWN_SYMBOL))); + return; + } + flags = JSTaggedValue(static_cast(flags.GetRawData() | IS_WELL_KNOWN_SYMBOL)); + SetFlags(thread, flags); + } + + bool IsInPublicSymbolTable() const + { + JSTaggedValue flags = this->GetFlags(); + ASSERT(flags.IsInteger()); + return (flags.GetRawData() & IS_IN_PUBLIC_SYMBOL_TABLE) != 0U; + } + + void SetInPublicSymbolTable(const JSThread *thread) + { + JSTaggedValue flags = this->GetFlags(); + if (flags.IsUndefined()) { + SetFlags(thread, JSTaggedValue(static_cast(IS_IN_PUBLIC_SYMBOL_TABLE))); + return; + } + flags = JSTaggedValue(static_cast(flags.GetRawData() | IS_IN_PUBLIC_SYMBOL_TABLE)); + SetFlags(thread, flags); + } + + bool IsInterestingSymbol() const + { + JSTaggedValue flags = this->GetFlags(); + ASSERT(flags.IsInteger()); + return (flags.GetRawData() & IS_INTERESTING_SYMBOL) != 0U; + } + + void SetInterestingSymbol(const JSThread *thread) + { + JSTaggedValue flags = this->GetFlags(); + if (flags.IsUndefined()) { + SetFlags(thread, JSTaggedValue(static_cast(IS_INTERESTING_SYMBOL))); + return; + } + flags = JSTaggedValue(static_cast(flags.GetRawData() | IS_INTERESTING_SYMBOL)); + SetFlags(thread, flags); + } + + bool IsPrivateNameSymbol() const + { + JSTaggedValue flags = this->GetFlags(); + ASSERT(flags.IsInteger()); + return (flags.GetRawData() & IS_PRIVATE_NAME) != 0U; + } + + void SetPrivateNameSymbol(const JSThread *thread) + { + JSTaggedValue flags = this->GetFlags(); + if (flags.IsUndefined()) { + SetFlags(thread, JSTaggedValue(static_cast(IS_PRIVATE_NAME))); + return; + } + flags = JSTaggedValue(static_cast(flags.GetRawData() | IS_PRIVATE_NAME)); + SetFlags(thread, flags); + } + + static bool Equal(const JSSymbol &src, const JSSymbol &dst) + { + if (src.GetFlags() != dst.GetFlags()) { + return false; + } + EcmaString *srcString = EcmaString::Cast(src.GetDescription().GetTaggedObject()); + EcmaString *dstString = EcmaString::Cast(dst.GetDescription().GetTaggedObject()); + return EcmaString::StringsAreEqual(srcString, dstString); + } + +public: + static constexpr size_t HASHFIELD_OFFSET = sizeof(TaggedObject); + ACCESSORS(HashField, HASHFIELD_OFFSET, FLAGS_OFFSET) + ACCESSORS(Flags, FLAGS_OFFSET, DESCRIPTION_OFFSET) + ACCESSORS(Description, DESCRIPTION_OFFSET, SIZE) + + DECL_DUMP() + + DECL_VISIT_OBJECT(HASHFIELD_OFFSET, SIZE) +}; +} // namespace ecmascript +} // namespace panda +#endif // PANDA_RUNTIME_ECMASCRIPT_NAME_H diff --git a/ecmascript/js_tagged_number.h b/ecmascript/js_tagged_number.h new file mode 100644 index 0000000000000000000000000000000000000000..26a249c8585c1d47a23bd530357275048549abd3 --- /dev/null +++ b/ecmascript/js_tagged_number.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_NUMBER_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_NUMBER_H + +#include "ecmascript/base/number_helper.h" + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" + +namespace panda { +namespace ecmascript { +class JSTaggedNumber final : public JSTaggedValue { +public: + constexpr JSTaggedNumber() = default; + explicit JSTaggedNumber(double v) : JSTaggedValue(v) {} + constexpr explicit JSTaggedNumber(int v) : JSTaggedValue(v) {} + explicit JSTaggedNumber(unsigned int v) : JSTaggedValue(v) {} + explicit JSTaggedNumber(JSTaggedValue v) : JSTaggedValue(v.GetRawData()) + { + ASSERT_PRINT(v.IsNumber(), "can not convert non Number JSTaggedValue to JSTaggedNumber"); + } + + ~JSTaggedNumber() = default; + DEFAULT_COPY_SEMANTIC(JSTaggedNumber); + DEFAULT_MOVE_SEMANTIC(JSTaggedNumber); + + static inline constexpr JSTaggedNumber Exception() + { + return JSTaggedNumber(VALUE_EXCEPTION); + } + + inline bool IsException() const + { + return JSTaggedValue::IsException(); + } + + inline int32_t ToInt32() const + { + if (IsInt()) { + return GetInt(); + } + return base::NumberHelper::DoubleToInt(GetDouble(), base::INT32_BITS); + } + + inline uint32_t ToUint32() const + { + return ToInt32(); + } + + inline int16_t ToInt16() const + { + return base::NumberHelper::DoubleToInt(GetNumber(), base::INT16_BITS); + } + + inline uint16_t ToUint16() const + { + return ToInt16(); + } + + inline int8_t ToInt8() const + { + return base::NumberHelper::DoubleToInt(GetNumber(), base::INT8_BITS); + } + + inline uint8_t ToUint8() const + { + return ToInt8(); + } + + inline JSHandle ToString(const JSThread *thread) const + { + return base::NumberHelper::NumberToString(thread, *this); + } + + JSTaggedNumber operator-(JSTaggedNumber number) const + { + if (IsInt() && number.IsInt()) { + int64_t a0 = GetInt(); + int64_t a1 = number.GetInt(); + int64_t res = a0 - a1; + if (res > INT32_MAX || res < INT32_MIN) { + return JSTaggedNumber(static_cast(res)); + } + return JSTaggedNumber(static_cast(res)); + } + return JSTaggedNumber(GetNumber() - number.GetNumber()); + } + + JSTaggedNumber operator*(JSTaggedNumber number) const + { + if (IsInt() && number.IsInt()) { + int64_t intA = GetInt(); + int64_t intB = number.GetInt(); + int64_t res = intA * intB; + if (res > INT32_MAX || res < INT32_MIN) { + return JSTaggedNumber(static_cast(res)); + } + return JSTaggedNumber(static_cast(res)); + } + return JSTaggedNumber(GetNumber() * number.GetNumber()); + } + + JSTaggedNumber operator++() const + { + if (IsInt()) { + int32_t value = GetInt(); + if (value == INT32_MAX) { + return JSTaggedNumber(static_cast(value) + 1.0); + } + return JSTaggedNumber(value + 1); + } + return JSTaggedNumber(GetDouble() + 1.0); + } + + JSTaggedNumber operator--() const + { + if (IsInt()) { + int32_t value = GetInt(); + if (value == INT32_MIN) { + return JSTaggedNumber(static_cast(value) - 1.0); + } + return JSTaggedNumber(value - 1); + } + return JSTaggedNumber(GetDouble() - 1.0); + } + + inline bool operator!=(const JSTaggedNumber &number) const + { + return GetNumber() != number.GetNumber(); + } + + /* static */ + inline static bool SameValue(JSTaggedNumber x, JSTaggedNumber y) + { + double xValue = x.GetNumber(); + double yValue = y.GetNumber(); + // SameNumberValue(NaN, NaN) is true. + if (xValue != yValue) { + return std::isnan(xValue) && std::isnan(yValue); + } + // SameNumberValue(0.0, -0.0) is false. + return (std::signbit(xValue) == std::signbit(yValue)); + } + + inline static JSTaggedNumber FromIntOrDouble(JSThread *thread, JSTaggedValue tagged) + { + if (tagged.IsInt() || tagged.IsDouble()) { + return JSTaggedNumber(tagged); + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert value to a number", JSTaggedNumber::Exception()); + } + +private: + constexpr explicit JSTaggedNumber(JSTaggedType v) : JSTaggedValue(v) {} +}; +} // namespace ecmascript +} // namespace panda +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_NUMBER_H diff --git a/ecmascript/js_tagged_value-inl.h b/ecmascript/js_tagged_value-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..406677d972b338752dc2ae23d6396da3057cca79 --- /dev/null +++ b/ecmascript/js_tagged_value-inl.h @@ -0,0 +1,908 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TAGGED_VALUE_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_TAGGED_VALUE_INL_H + +#include "ecmascript/js_tagged_value.h" + +#include "ecmascript/accessor_data.h" +#include "ecmascript/base/error_helper.h" +#include "ecmascript/base/number_helper.h" +#include "ecmascript/base/string_helper.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/js_object.h" +#include "ecmascript/js_proxy.h" +#include "ecmascript/js_symbol.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/mem/tagged_object-inl.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +// ecma6 7.1 Type Conversion +static constexpr double SAFE_NUMBER = 9007199254740991LL; +static constexpr uint32_t MAX_INDEX_LEN = 10; + +inline bool JSTaggedValue::ToBoolean() const +{ + if (IsInt()) { + return GetInt() != 0; + } + if (IsDouble()) { + double d = GetDouble(); + return !std::isnan(d) && d != 0; + } + + switch (GetRawData()) { + case JSTaggedValue::VALUE_UNDEFINED: + [[fallthrough]]; + case JSTaggedValue::VALUE_HOLE: + [[fallthrough]]; + case JSTaggedValue::VALUE_NULL: + [[fallthrough]]; + case JSTaggedValue::VALUE_FALSE: { + return false; + } + case JSTaggedValue::VALUE_TRUE: { + return true; + } + default: { + break; + } + } + + if (IsHeapObject()) { + TaggedObject *obj = GetTaggedObject(); + if (IsString()) { + auto str = static_cast(obj); + return str->GetLength() != 0; + } + + return true; + } + + UNREACHABLE(); +} + +inline JSTaggedNumber JSTaggedValue::ToNumber(JSThread *thread, const JSHandle &tagged) +{ + if (tagged->IsInt() || tagged->IsDouble()) { + return JSTaggedNumber(tagged.GetTaggedValue()); + } + + switch (tagged->GetRawData()) { + case JSTaggedValue::VALUE_UNDEFINED: + case JSTaggedValue::VALUE_HOLE: { + return JSTaggedNumber(base::NAN_VALUE); + } + case JSTaggedValue::VALUE_TRUE: { + return JSTaggedNumber(1); + } + case JSTaggedValue::VALUE_FALSE: + case JSTaggedValue::VALUE_NULL: { + return JSTaggedNumber(0); + } + default: { + break; + } + } + + if (tagged->IsString()) { + Span str; + auto strObj = static_cast(tagged->GetTaggedObject()); + size_t strLen = strObj->GetLength(); + if (strLen == 0) { + return JSTaggedNumber(0); + } + [[maybe_unused]] CVector buf; // Span will use buf.data(), shouldn't define inside 'if' + if (UNLIKELY(strObj->IsUtf16())) { + size_t len = base::utf_helper::Utf16ToUtf8Size(strObj->GetDataUtf16(), strLen) - 1; + buf.reserve(len); + len = base::utf_helper::ConvertRegionUtf16ToUtf8(strObj->GetDataUtf16(), buf.data(), strLen, len, 0); + str = Span(buf.data(), len); + } else { + str = Span(strObj->GetDataUtf8(), strLen); + } + double d = base::NumberHelper::StringToDouble(str.begin(), str.end(), 0, + base::ALLOW_BINARY + base::ALLOW_OCTAL + base::ALLOW_HEX); + return JSTaggedNumber(d); + } + if (tagged->IsECMAObject()) { + JSHandle primValue(thread, ToPrimitive(thread, tagged, PREFER_NUMBER)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedNumber::Exception()); + return ToNumber(thread, primValue); + } + if (tagged->IsSymbol()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Symbol value to a number", JSTaggedNumber::Exception()); + } + + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Unknown value to a number", JSTaggedNumber::Exception()); +} + +inline JSTaggedNumber JSTaggedValue::ToInteger(JSThread *thread, const JSHandle &tagged) +{ + JSTaggedNumber number = ToNumber(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedNumber::Exception()); + + return JSTaggedNumber(base::NumberHelper::TruncateDouble(number.GetNumber())); +} + +inline int32_t JSTaggedValue::ToInt32(JSThread *thread, const JSHandle &tagged) +{ + JSTaggedNumber number = ToNumber(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + return base::NumberHelper::DoubleToInt(number.GetNumber(), base::INT32_BITS); +} + +inline uint32_t JSTaggedValue::ToUint32(JSThread *thread, const JSHandle &tagged) +{ + return ToInt32(thread, tagged); +} + +inline int16_t JSTaggedValue::ToInt16(JSThread *thread, const JSHandle &tagged) +{ + JSTaggedNumber number = ToNumber(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + + return base::NumberHelper::DoubleToInt(number.GetNumber(), base::INT16_BITS); +} + +inline uint16_t JSTaggedValue::ToUint16(JSThread *thread, const JSHandle &tagged) +{ + return ToInt16(thread, tagged); +} + +inline int8_t JSTaggedValue::ToInt8(JSThread *thread, const JSHandle &tagged) +{ + JSTaggedNumber number = ToNumber(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + + return base::NumberHelper::DoubleToInt(number.GetNumber(), base::INT8_BITS); +} + +inline uint8_t JSTaggedValue::ToUint8(JSThread *thread, const JSHandle &tagged) +{ + return ToInt8(thread, tagged); +} + +inline uint8_t JSTaggedValue::ToUint8Clamp(JSThread *thread, const JSHandle &tagged) +{ + JSTaggedNumber number = ToNumber(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + + double d = number.GetNumber(); + if (std::isnan(d) || d <= 0) { + return 0; + } + if (d >= UINT8_MAX) { + return UINT8_MAX; + } + + return lrint(d); +} + +inline JSTaggedNumber JSTaggedValue::ToLength(JSThread *thread, const JSHandle &tagged) +{ + JSTaggedNumber len = ToInteger(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedNumber::Exception()); + if (len.GetNumber() < 0.0) { + return JSTaggedNumber(static_cast(0)); + } + if (len.GetNumber() > SAFE_NUMBER) { + return JSTaggedNumber(static_cast(SAFE_NUMBER)); + } + return len; +} + +// ecma6 7.2 Testing and Comparison Operations +inline JSHandle JSTaggedValue::RequireObjectCoercible(JSThread *thread, + const JSHandle &tagged) +{ + if (tagged->IsUndefined() || tagged->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "RequireObjectCoercible", + JSHandle(thread, JSTaggedValue::Exception())); + } + return tagged; +} + +inline bool JSTaggedValue::IsCallable() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsCallable(); +} + +inline bool JSTaggedValue::IsConstructor() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsConstructor(); +} + +inline bool JSTaggedValue::IsExtensible(JSThread *thread) const +{ + if (UNLIKELY(IsJSProxy())) { + return JSProxy::IsExtensible(thread, JSHandle(thread, *this)); + } + + return IsHeapObject() && GetTaggedObject()->GetClass()->IsExtensible(); +} + +inline bool JSTaggedValue::IsClassConstructor() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsClassConstructor(); +} + +inline bool JSTaggedValue::IsClassPrototype() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsClassPrototype(); +} + +inline bool JSTaggedValue::IsPropertyKey(const JSHandle &key) +{ + return key->IsStringOrSymbol() || key->IsNumber(); +} + +inline bool JSTaggedValue::SameValue(const JSTaggedValue &x, const JSTaggedValue &y) +{ + // same object or special type must be same value + if (x == y) { + return true; + } + + if (x.IsNumber() && y.IsNumber()) { + return SameValueNumberic(x, y); + } + + if (x.IsString() && y.IsString()) { + return EcmaString::StringsAreEqual(static_cast(x.GetTaggedObject()), + static_cast(y.GetTaggedObject())); + } + return false; +} + +inline bool JSTaggedValue::SameValue(const JSHandle &xHandle, const JSHandle &yHandle) +{ + return SameValue(xHandle.GetTaggedValue(), yHandle.GetTaggedValue()); +} + +inline bool JSTaggedValue::SameValueZero(const JSTaggedValue &x, const JSTaggedValue &y) +{ + if (x == y) { + return true; + } + + if (x.IsNumber() && y.IsNumber()) { + double xValue = x.ExtractNumber(); + double yValue = y.ExtractNumber(); + // Compare xValue with yValue to deal with -0.0 + return (xValue == yValue) || (std::isnan(xValue) && std::isnan(yValue)); + } + + if (x.IsString() && y.IsString()) { + return EcmaString::StringsAreEqual(static_cast(x.GetTaggedObject()), + static_cast(y.GetTaggedObject())); + } + return false; +} + +inline bool JSTaggedValue::SameValueNumberic(const JSTaggedValue &x, const JSTaggedValue &y) +{ + double xValue = x.ExtractNumber(); + double yValue = y.ExtractNumber(); + // SameNumberValue(NaN, NaN) is true. + if (xValue != yValue) { + return std::isnan(xValue) && std::isnan(yValue); + } + // SameNumberValue(0.0, -0.0) is false. + return (std::signbit(xValue) == std::signbit(yValue)); +} + +inline bool JSTaggedValue::Less(JSThread *thread, const JSHandle &x, const JSHandle &y) +{ + ComparisonResult result = Compare(thread, x, y); + return result == ComparisonResult::LESS; +} + +inline bool JSTaggedValue::StrictNumberEquals(double x, double y) +{ + // Must check explicitly for NaN's on Windows, but -0 works fine. + if (std::isnan(x) || std::isnan(y)) { + return false; + } + return x == y; +} + +inline bool JSTaggedValue::StrictEqual([[maybe_unused]] const JSThread *thread, const JSHandle &x, + const JSHandle &y) +{ + if (x->IsNumber() && y->IsNumber()) { + return StrictNumberEquals(x->ExtractNumber(), y->ExtractNumber()); + } + + if (x.GetTaggedValue() == y.GetTaggedValue()) { + return true; + } + + if (x->IsString() && y->IsString()) { + return EcmaString::StringsAreEqual(x.GetObject(), y.GetObject()); + } + return false; +} + +inline ComparisonResult JSTaggedValue::StrictNumberCompare(double x, double y) +{ + if (std::isnan(x) || std::isnan(y)) { + return ComparisonResult::UNDEFINED; + } + if (x < y) { + return ComparisonResult::LESS; + } + if (x > y) { + return ComparisonResult::GREAT; + } + return ComparisonResult::EQUAL; +} + +inline bool JSTaggedValue::IsNumber() const +{ + return IsInt() || IsDouble(); +} + +inline bool JSTaggedValue::IsString() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsString(); +} + +inline bool JSTaggedValue::IsStringOrSymbol() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsStringOrSymbol(); +} + +inline bool JSTaggedValue::IsTaggedArray() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsTaggedArray(); +} + +inline bool JSTaggedValue::IsNativePointer() const +{ + return IsJSNativePointer(); +} + +inline bool JSTaggedValue::IsJSNativeObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSNativeObject(); +} + +inline bool JSTaggedValue::IsJSNativePointer() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSNativePointer(); +} + +inline bool JSTaggedValue::IsSymbol() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSSymbol(); +} + +inline bool JSTaggedValue::IsJSProxy() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSProxy(); +} + +inline bool JSTaggedValue::IsBoolean() const +{ + return IsTrue() || IsFalse(); +} + +inline bool JSTaggedValue::IsJSObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSObject(); +} + +inline bool JSTaggedValue::IsECMAObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsECMAObject(); +} + +inline bool JSTaggedValue::IsJSPromise() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSPromise(); +} + +inline bool JSTaggedValue::IsRecord() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsRecord(); +} + +inline bool JSTaggedValue::IsPromiseReaction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPromiseReaction(); +} + +inline bool JSTaggedValue::IsJSPromiseReactionFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSPromiseReactionFunction(); +} + +inline bool JSTaggedValue::IsProgram() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsProgram(); +} + +inline bool JSTaggedValue::IsLexicalFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsLexicalFunction(); +} + +inline bool JSTaggedValue::IsJSPromiseExecutorFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSPromiseExecutorFunction(); +} + +inline bool JSTaggedValue::IsJSPromiseAllResolveElementFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSPromiseAllResolveElementFunction(); +} + +inline bool JSTaggedValue::IsCompletionRecord() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsCompletionRecord(); +} + +inline bool JSTaggedValue::IsResolvingFunctionsRecord() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsResolvingFunctionsRecord(); +} + +inline bool JSTaggedValue::IsPromiseRecord() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPromiseRecord(); +} + +inline bool JSTaggedValue::IsPromiseIteratorRecord() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPromiseIteratorRecord(); +} + +inline bool JSTaggedValue::IsPromiseCapability() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPromiseCapability(); +} + +inline bool JSTaggedValue::IsJSError() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSError(); +} + +inline bool JSTaggedValue::IsJSFunctionExtraInfo() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSFunctionExtraInfo(); +} + +inline bool JSTaggedValue::IsMicroJobQueue() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsMicroJobQueue(); +} + +inline bool JSTaggedValue::IsPendingJob() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPendingJob(); +} + +inline bool JSTaggedValue::IsArguments() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsArguments(); +} + +inline bool JSTaggedValue::IsDate() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsDate(); +} + +inline bool JSTaggedValue::IsArray(JSThread *thread) const +{ + if (!IsHeapObject()) { + return false; + } + JSHClass *jsHclass = GetTaggedObject()->GetClass(); + if (jsHclass->IsJSArray()) { + return true; + } + + if (jsHclass->IsJSProxy()) { + return JSProxy::Cast(GetTaggedObject())->IsArray(thread); + } + return false; +} + +inline bool JSTaggedValue::IsJSArray() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSArray(); +} + +inline bool JSTaggedValue::IsStableJSArray(JSThread *thread) const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsStableJSArray() && + !thread->IsStableArrayElementsGuardiansInvalid(); +} + +inline bool JSTaggedValue::IsTypedArray() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsTypedArray(); +} + +inline bool JSTaggedValue::IsJSTypedArray() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSTypedArray(); +} + +inline bool JSTaggedValue::IsJSInt8Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSInt8Array(); +} + +inline bool JSTaggedValue::IsJSUint8Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSUint8Array(); +} + +inline bool JSTaggedValue::IsJSUint8ClampedArray() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSUint8ClampedArray(); +} + +inline bool JSTaggedValue::IsJSInt16Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSInt16Array(); +} + +inline bool JSTaggedValue::IsJSUint16Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSUint16Array(); +} + +inline bool JSTaggedValue::IsJSInt32Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSInt32Array(); +} + +inline bool JSTaggedValue::IsJSUint32Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSUint32Array(); +} + +inline bool JSTaggedValue::IsJSFloat32Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSFloat32Array(); +} + +inline bool JSTaggedValue::IsJSFloat64Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSFloat64Array(); +} + +inline bool JSTaggedValue::IsJSMap() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSMap(); +} + +inline bool JSTaggedValue::IsJSWeakMap() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSWeakMap(); +} + +inline bool JSTaggedValue::IsJSWeakSet() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSWeakSet(); +} + +inline bool JSTaggedValue::IsJSSet() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSSet(); +} + +inline bool JSTaggedValue::IsJSRegExp() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSRegExp(); +} + +inline bool JSTaggedValue::IsJSFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSFunction(); +} + +inline bool JSTaggedValue::IsJSFunctionBase() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSFunctionBase(); +} + +inline bool JSTaggedValue::IsBoundFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJsBoundFunction(); +} + +inline bool JSTaggedValue::IsProxyRevocFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSProxyRevocFunction(); +} + +inline bool JSTaggedValue::IsJSAsyncFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAsyncFunction(); +} + +inline bool JSTaggedValue::IsJSAsyncAwaitStatusFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAsyncAwaitStatusFunction(); +} + +inline bool JSTaggedValue::IsJSPrimitiveRef() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJsPrimitiveRef(); +} + +inline bool JSTaggedValue::IsJSPrimitive() const +{ + return IsNumber() || IsStringOrSymbol() || IsBoolean(); +} + +inline bool JSTaggedValue::IsAccessorData() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsAccessorData(); +} + +inline bool JSTaggedValue::IsInternalAccessor() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsInternalAccessor(); +} + +inline bool JSTaggedValue::IsAccessor() const +{ + if (IsHeapObject()) { + auto *jshcalss = GetTaggedObject()->GetClass(); + return jshcalss->IsAccessorData() || jshcalss->IsInternalAccessor(); + } + + return false; +} + +inline bool JSTaggedValue::IsPrototypeHandler() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPrototypeHandler(); +} + +inline bool JSTaggedValue::IsTransitionHandler() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsTransitionHandler(); +} + +inline bool JSTaggedValue::IsPropertyBox() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPropertyBox(); +} + +inline bool JSTaggedValue::IsProtoChangeDetails() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsProtoChangeDetails(); +} +inline bool JSTaggedValue::IsProtoChangeMarker() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsProtoChangeMarker(); +} + +inline bool JSTaggedValue::IsJSGlobalEnv() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJsGlobalEnv(); +} + +inline bool JSTaggedValue::IsForinIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsForinIterator(); +} + +inline bool JSTaggedValue::IsJSSetIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSSetIterator(); +} + +inline bool JSTaggedValue::IsJSMapIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSMapIterator(); +} + +inline bool JSTaggedValue::IsJSArrayIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSArrayIterator(); +} + +inline bool JSTaggedValue::IsIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsIterator(); +} + +inline bool JSTaggedValue::IsGeneratorFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsGeneratorFunction(); +} +inline bool JSTaggedValue::IsGeneratorObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsGeneratorObject(); +} + +inline bool JSTaggedValue::IsAsyncFuncObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsAsyncFuncObject(); +} + +inline bool JSTaggedValue::IsDynClass() const +{ + return IsJSHClass(); +} + +inline bool JSTaggedValue::IsJSHClass() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsHClass(); +} + +inline bool JSTaggedValue::IsObjectWrapper() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsObjectWrapper(); +} + +inline bool JSTaggedValue::IsStringIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsStringIterator(); +} + +inline bool JSTaggedValue::IsArrayBuffer() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsArrayBuffer(); +} + +inline bool JSTaggedValue::IsDataView() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsDataView(); +} + +inline bool JSTaggedValue::IsTemplateMap() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsTemplateMap(); +} + +inline bool JSTaggedValue::IsJSGlobalObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSGlobalObject(); +} + +inline double JSTaggedValue::ExtractNumber() const +{ + ASSERT(IsNumber()); + return GetNumber(); +} + +// 9.4.2.4 ArraySetLength 3 to 7 +inline bool JSTaggedValue::ToArrayLength(JSThread *thread, const JSHandle &tagged, uint32_t *output) +{ + // 3. Let newLen be ToUint32(Desc.[[Value]]). + uint32_t newLen = ToUint32(thread, tagged); + // 4. ReturnIfAbrupt(newLen). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 5. Let numberLen be ToNumber(Desc.[[Value]]). + JSTaggedNumber numberLen = ToNumber(thread, tagged); + // 6. ReturnIfAbrupt(newLen). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 7. If newLen != numberLen, throw a RangeError exception. + if (JSTaggedNumber(newLen) != numberLen) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Not a valid array length", false); + } + + *output = newLen; + return true; +} + +inline array_size_t JSTaggedValue::GetArrayLength() const +{ + ASSERT(IsNumber()); + if (IsInt()) { + return static_cast(GetInt()); + } + if (IsDouble()) { + ASSERT(GetDouble() <= TaggedArray::MAX_ARRAY_INDEX); + return static_cast(GetDouble()); + } + UNREACHABLE(); +} + +inline bool JSTaggedValue::ToElementIndex(JSTaggedValue key, uint32_t *output) +{ + if (key.IsInt()) { + int index = key.GetInt(); + if (index >= 0) { + *output = index; + return true; + } + } else if (key.IsDouble()) { + double d = key.GetDouble(); + uint32_t index = base::NumberHelper::DoubleToInt(d, base::INT32_BITS); + if (d - static_cast(index) == 0.0) { + *output = index; + return true; + } + } else if (key.IsString()) { + return StringToElementIndex(key, output); + } + return false; +} + +inline bool JSTaggedValue::StringToElementIndex(JSTaggedValue key, uint32_t *output) +{ + ASSERT(key.IsString()); + + auto strObj = static_cast(key.GetTaggedObject()); + uint32_t len = strObj->GetLength(); + if (len == 0 || len > MAX_INDEX_LEN) { // NOLINTNEXTLINEreadability-magic-numbers) + return false; + } + uint32_t c; + if (strObj->IsUtf16()) { + c = strObj->GetDataUtf16()[0]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else { + c = strObj->GetDataUtf8()[0]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + uint64_t n = 0; + if (c >= '0' && c <= '9') { + if (c == '0') { + if (len != 1) { + return false; + } + *output = 0; + return true; + } + + n = c - '0'; + for (uint32_t i = 1; i < len; i++) { + if (strObj->IsUtf16()) { + c = strObj->GetDataUtf16()[i]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else { + c = strObj->GetDataUtf8()[i]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + if (c < '0' || c > '9') { + return false; + } + // NOLINTNEXTLINE(readability-magic-numbers) + n = n * 10 + (c - '0'); // 10: decimal factor + } + if (n < JSObject::MAX_ELEMENT_INDEX) { + *output = n; + return true; + } + } + return false; +} + +inline uint32_t JSTaggedValue::GetKeyHashCode() const +{ + ASSERT(IsStringOrSymbol()); + if (IsString()) { + return EcmaString::Cast(GetTaggedObject())->GetHashcode(); + } + + return static_cast(JSSymbol::Cast(GetTaggedObject())->GetHashField().GetInt()); +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_TAGGED_VALUE__INL_H diff --git a/ecmascript/js_tagged_value.cpp b/ecmascript/js_tagged_value.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e851beb87825addb7b6837fdc6b8379c88060d6f --- /dev/null +++ b/ecmascript/js_tagged_value.cpp @@ -0,0 +1,753 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_proxy.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/js_typed_array.h" +#include "ecmascript/tagged_array.h" +#include "js_object-inl.h" +#include "object_factory.h" + +namespace panda::ecmascript { +JSHandle GetTypeString(JSThread *thread, PreferredPrimitiveType type) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (type == NO_PREFERENCE) { + return factory->NewFromString("default"); + } + if (type == PREFER_NUMBER) { + return factory->NewFromString("number"); + } + return factory->NewFromString("string"); +} + +JSHandle JSTaggedValue::ToPropertyKey(JSThread *thread, const JSHandle &tagged) +{ + if (tagged->IsStringOrSymbol() || tagged->IsNumber()) { + return tagged; + } + JSHandle key(thread, ToPrimitive(thread, tagged, PREFER_STRING)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + if (key->IsSymbol()) { + return key; + } + JSHandle string = ToString(thread, key); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + return JSHandle::Cast(string); +} + +bool JSTaggedValue::IsInteger() const +{ + if (!IsNumber()) { + return false; + } + + if (IsInt()) { + return true; + } + + double thisValue = GetDouble(); + // If argument is NaN, +∞, or -∞, return false. + if (!std::isfinite(thisValue)) { + return false; + } + + // If floor(abs(argument)) ≠ abs(argument), return false. + if (std::floor(std::abs(thisValue)) != std::abs(thisValue)) { + return false; + } + + return true; +} + +bool JSTaggedValue::WithinInt32() const +{ + if (!IsNumber()) { + return false; + } + + double doubleValue = GetNumber(); + if (bit_cast(doubleValue) == bit_cast(-0.0)) { + return false; + } + + int32_t intvalue = base::NumberHelper::DoubleToInt(doubleValue, base::INT32_BITS); + return doubleValue == static_cast(intvalue); +} + +bool JSTaggedValue::IsZero() const +{ + if (GetRawData() == VALUE_ZERO) { + return true; + } + if (IsDouble()) { + const double limit = 1e-8; + return (std::abs(GetDouble() - 0.0) <= limit); + } + return false; +} + +bool JSTaggedValue::Equal(JSThread *thread, const JSHandle &x, const JSHandle &y) +{ + if (x->IsNumber()) { + if (y->IsNumber()) { + return StrictNumberEquals(x->ExtractNumber(), y->ExtractNumber()); + } + if (y->IsString()) { + JSTaggedNumber yNumber = ToNumber(thread, y); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return StrictNumberEquals(x->ExtractNumber(), yNumber.GetNumber()); + } + if (y->IsBoolean()) { + JSTaggedNumber yNumber = ToNumber(thread, y); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return StrictNumberEquals(x->ExtractNumber(), yNumber.GetNumber()); + } + if (y->IsHeapObject() && !y->IsSymbol()) { + JSHandle yPrimitive(thread, ToPrimitive(thread, y)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return Equal(thread, x, yPrimitive); + } + return false; + } + + if (x->IsString()) { + if (y->IsString()) { + return EcmaString::StringsAreEqual(static_cast(x->GetTaggedObject()), + static_cast(y->GetTaggedObject())); + } + if (y->IsNumber()) { + JSTaggedNumber xNumber = ToNumber(thread, x); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return StrictNumberEquals(xNumber.GetNumber(), y->ExtractNumber()); + } + if (y->IsBoolean()) { + JSTaggedNumber xNumber = ToNumber(thread, x); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + JSTaggedNumber yNumber = ToNumber(thread, y); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return StrictNumberEquals(xNumber.GetNumber(), yNumber.GetNumber()); + } + if (y->IsHeapObject() && !y->IsSymbol()) { + JSHandle yPrimitive(thread, ToPrimitive(thread, y)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return Equal(thread, x, yPrimitive); + } + return false; + } + + if (x->IsBoolean()) { + JSTaggedNumber xNumber = ToNumber(thread, x); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return Equal(thread, JSHandle(thread, xNumber), y); + } + + if (x->IsSymbol()) { + if (y->IsSymbol()) { + return x.GetTaggedValue() == y.GetTaggedValue(); + } + if (y->IsHeapObject()) { + JSHandle yPrimitive(thread, ToPrimitive(thread, y)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return Equal(thread, x, yPrimitive); + } + return false; + } + + if (x->IsHeapObject()) { + if (y->IsHeapObject()) { + // if same type, must call Type::StrictEqual() + JSType xType = x.GetTaggedValue().GetTaggedObject()->GetClass()->GetObjectType(); + JSType yType = y.GetTaggedValue().GetTaggedObject()->GetClass()->GetObjectType(); + if (xType == yType) { + return StrictEqual(thread, x, y); + } + } + if (y->IsNumber() || y->IsStringOrSymbol() || y->IsBoolean()) { + JSHandle x_primitive(thread, ToPrimitive(thread, x)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return Equal(thread, x_primitive, y); + } + return false; + } + + if (x->IsNull() && y->IsNull()) { + return true; + } + + if (x->IsUndefined() && y->IsUndefined()) { + return true; + } + + if (x->IsNull() && y->IsUndefined()) { + return true; + } + + if (x->IsUndefined() && y->IsNull()) { + return true; + } + + return false; +} + +ComparisonResult JSTaggedValue::Compare(JSThread *thread, const JSHandle &x, + const JSHandle &y) +{ + JSHandle primX(thread, ToPrimitive(thread, x)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + JSHandle primY(thread, ToPrimitive(thread, y)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + + if (primX->IsString() && primY->IsString()) { + auto xString = static_cast(primX->GetTaggedObject()); + auto yString = static_cast(primY->GetTaggedObject()); + int result = xString->Compare(yString); + if (result < 0) { + return ComparisonResult::LESS; + } + if (result == 0) { + return ComparisonResult::EQUAL; + } + return ComparisonResult::GREAT; + } + + JSTaggedNumber xNumber = ToNumber(thread, x); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + JSTaggedNumber yNumber = ToNumber(thread, y); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + return StrictNumberCompare(xNumber.GetNumber(), yNumber.GetNumber()); +} + +bool JSTaggedValue::IsSameTypeOrHClass(JSTaggedValue x, JSTaggedValue y) +{ + if (x.IsNumber() && y.IsNumber()) { + return true; + } + if (x.IsBoolean() && y.IsBoolean()) { + return true; + } + if (x.IsString() && y.IsString()) { + return true; + } + if (x.IsHeapObject() && y.IsHeapObject()) { + return x.GetTaggedObject()->GetClass() == y.GetTaggedObject()->GetClass(); + } + + return false; +} + +JSTaggedValue JSTaggedValue::ToPrimitive(JSThread *thread, const JSHandle &tagged, + PreferredPrimitiveType type) +{ + if (tagged->IsECMAObject()) { + JSHandle object(tagged); + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle keyString = vm->GetGlobalEnv()->GetToPrimitiveSymbol(); + + JSHandle exoticToprim = JSObject::GetProperty(thread, object, keyString).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + if (!exoticToprim->IsUndefined()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle argv = factory->NewTaggedArray(1); + JSTaggedValue value = GetTypeString(thread, type).GetTaggedValue(); + argv->Set(thread, 0, value); + JSTaggedValue valueResult = JSFunction::Call(thread, exoticToprim, tagged, argv); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + if (!valueResult.IsECMAObject()) { + return valueResult; + } + THROW_TYPE_ERROR_AND_RETURN(thread, "", JSTaggedValue::Exception()); + } else { + type = (type == NO_PREFERENCE) ? PREFER_NUMBER : type; + return OrdinaryToPrimitive(thread, tagged, type); + } + } + return tagged.GetTaggedValue(); +} + +JSTaggedValue JSTaggedValue::OrdinaryToPrimitive(JSThread *thread, const JSHandle &tagged, + PreferredPrimitiveType type) +{ + static_assert(PREFER_NUMBER == 0 && PREFER_STRING == 1); + ASSERT(tagged->IsECMAObject()); + auto globalConst = thread->GlobalConstants(); + for (uint8_t i = 0; i < 2; i++) { // 2: 2 means value has 2 target types, string or value. + JSHandle keyString; + if ((type ^ i) != 0) { + keyString = globalConst->GetHandledToStringString(); + } else { + keyString = globalConst->GetHandledValueOfString(); + } + JSHandle entryfunc = JSObject::GetProperty(thread, tagged, keyString).GetValue(); + if (entryfunc->IsCallable()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSTaggedValue valueResult = JSFunction::Call(thread, entryfunc, tagged, factory->EmptyArray()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + if (!valueResult.IsECMAObject()) { + return valueResult; + } + } + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a illegal value to a Primitive", JSTaggedValue::Undefined()); +} + +JSHandle JSTaggedValue::ToString(JSThread *thread, const JSHandle &tagged) +{ + if (tagged->IsString()) { + return JSHandle(tagged); + } + auto globalConst = thread->GlobalConstants(); + if (tagged->IsSpecial()) { + switch (tagged->GetRawData()) { + case VALUE_UNDEFINED: { + return JSHandle(globalConst->GetHandledUndefinedString()); + } + case VALUE_NULL: { + return JSHandle(globalConst->GetHandledNullString()); + } + case VALUE_TRUE: { + return JSHandle(globalConst->GetHandledTrueString()); + } + case VALUE_FALSE: { + return JSHandle(globalConst->GetHandledFalseString()); + } + case VALUE_HOLE: { + return JSHandle(globalConst->GetHandledEmptyString()); + } + default: + break; + } + } + + if (tagged->IsNumber()) { + return base::NumberHelper::NumberToString(thread, tagged.GetTaggedValue()); + } + + auto emptyStr = globalConst->GetHandledEmptyString(); + if (tagged->IsECMAObject()) { + JSHandle primValue(thread, ToPrimitive(thread, tagged, PREFER_STRING)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(emptyStr)); + return ToString(thread, primValue); + } + // Already Include Symbol + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a illegal value to a String", JSHandle(emptyStr)); +} + +JSTaggedValue JSTaggedValue::CanonicalNumericIndexString(JSThread *thread, const JSHandle &tagged) +{ + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromString("-0"); + if (tagged->IsString()) { + if (EcmaString::StringsAreEqual(static_cast(tagged->GetTaggedObject()), *str)) { + return JSTaggedValue(-0.0); + } + JSHandle tmp(thread, ToNumber(thread, tagged)); + if (SameValue(ToString(thread, tmp).GetTaggedValue(), tagged.GetTaggedValue())) { + return tmp.GetTaggedValue(); + } + } + return JSTaggedValue::Undefined(); +} + +JSHandle JSTaggedValue::ToObject(JSThread *thread, const JSHandle &tagged) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (tagged->IsInt() || tagged->IsDouble()) { + return JSHandle::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_NUMBER, tagged)); + } + + switch (tagged->GetRawData()) { + case JSTaggedValue::VALUE_UNDEFINED: { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a UNDEFINED value to a JSObject", + JSHandle(thread, JSTaggedValue::Exception())); + } + case JSTaggedValue::VALUE_HOLE: { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a HOLE value to a JSObject", + JSHandle(thread, JSTaggedValue::Exception())); + } + case JSTaggedValue::VALUE_NULL: { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a NULL value to a JSObject", + JSHandle(thread, JSTaggedValue::Exception())); + } + case JSTaggedValue::VALUE_TRUE: + case JSTaggedValue::VALUE_FALSE: { + return JSHandle::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_BOOLEAN, tagged)); + } + default: { + break; + } + } + + if (tagged->IsECMAObject()) { + return JSHandle::Cast(tagged); + } + if (tagged->IsSymbol()) { + return JSHandle::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_SYMBOL, tagged)); + } + if (tagged->IsString()) { + return JSHandle::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_STRING, tagged)); + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Unknown object value to a JSObject", + JSHandle(thread, JSTaggedValue::Exception())); +} + +// 7.3.1 Get ( O, P ) +OperationResult JSTaggedValue::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a valid object", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + if (obj->IsJSProxy()) { + return JSProxy::GetProperty(thread, JSHandle(obj), key); + } + if (obj->IsTypedArray()) { + return JSTypedArray::GetProperty(thread, obj, JSTypedArray::ToPropKey(thread, key)); + } + + return JSObject::GetProperty(thread, obj, key); +} + +OperationResult JSTaggedValue::GetProperty(JSThread *thread, const JSHandle &obj, uint32_t key) +{ + if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a valid object", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + + if (obj->IsJSProxy()) { + JSHandle keyHandle(thread, JSTaggedValue(key)); + return JSProxy::GetProperty(thread, JSHandle(obj), keyHandle); + } + + if (obj->IsTypedArray()) { + return JSTypedArray::GetProperty(thread, obj, key); + } + + return JSObject::GetProperty(thread, obj, key); +} + +OperationResult JSTaggedValue::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &receiver) +{ + if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a valid object", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + if (obj->IsJSProxy()) { + return JSProxy::GetProperty(thread, JSHandle(obj), key, receiver); + } + if (obj->IsTypedArray()) { + return JSTypedArray::GetProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), receiver); + } + + return JSObject::GetProperty(thread, obj, key, receiver); +} + +// 7.3.3 Set (O, P, V, Throw) +bool JSTaggedValue::SetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value, bool mayThrow) +{ + if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a Valid object", false); + } + + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + // 4. Let success be O.[[Set]](P, V, O). + bool success = false; + if (obj->IsJSProxy()) { + success = JSProxy::SetProperty(thread, JSHandle(obj), key, value, mayThrow); + } else if (obj->IsTypedArray()) { + success = JSTypedArray::SetProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), value, mayThrow); + } else { + success = JSObject::SetProperty(thread, obj, key, value, mayThrow); + } + // 5. ReturnIfAbrupt(success). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, success); + // 6. If success is false and Throw is true, throw a TypeError exception. + // have done in JSObject::SetPropert. + return success; +} + +bool JSTaggedValue::SetProperty(JSThread *thread, const JSHandle &obj, uint32_t key, + const JSHandle &value, bool mayThrow) +{ + if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a Valid object", false); + } + + // 4. Let success be O.[[Set]](P, V, O). + bool success = false; + if (obj->IsJSProxy()) { + JSHandle keyHandle(thread, JSTaggedValue(key)); + success = JSProxy::SetProperty(thread, JSHandle(obj), keyHandle, value, mayThrow); + } else if (obj->IsTypedArray()) { + JSHandle keyHandle(thread, JSTaggedValue(key)); + success = JSTypedArray::SetProperty( + thread, obj, JSHandle(JSTaggedValue::ToString(thread, keyHandle)), value, mayThrow); + } else { + success = JSObject::SetProperty(thread, obj, key, value, mayThrow); + } + // 5. ReturnIfAbrupt(success). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, success); + // 6. If success is false and Throw is true, throw a TypeError exception. + // have done in JSObject::SetPropert. + return success; +} + +bool JSTaggedValue::SetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value, + const JSHandle &receiver, bool mayThrow) +{ + if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a Valid object", false); + } + + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + // 4. Let success be O.[[Set]](P, V, O). + bool success = false; + if (obj->IsJSProxy()) { + success = JSProxy::SetProperty(thread, JSHandle(obj), key, value, receiver, mayThrow); + } else if (obj->IsTypedArray()) { + success = + JSTypedArray::SetProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), value, receiver, mayThrow); + } else { + success = JSObject::SetProperty(thread, obj, key, value, receiver, mayThrow); + } + // 5. ReturnIfAbrupt(success). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, success); + // 6. If success is false and Throw is true, throw a TypeError exception. + // have done in JSObject::SetPropert. + return success; +} + +bool JSTaggedValue::DeleteProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + if (obj->IsJSProxy()) { + return JSProxy::DeleteProperty(thread, JSHandle(obj), key); + } + + return JSObject::DeleteProperty(thread, JSHandle(obj), key); +} + +// 7.3.8 DeletePropertyOrThrow (O, P) +bool JSTaggedValue::DeletePropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + if (!obj->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a valid object", false); + } + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + // 3. Let success be O.[[Delete]](P). + bool success = DeleteProperty(thread, obj, key); + + // 4. ReturnIfAbrupt(success). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, success); + // 5. If success is false, throw a TypeError exception + if (!success) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot delete property", false); + } + return success; +} + +// 7.3.7 DefinePropertyOrThrow (O, P, desc) +bool JSTaggedValue::DefinePropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc) +{ + // 1. Assert: Type(O) is Object. + // 2. Assert: IsPropertyKey(P) is true. + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + // 3. Let success be ? O.[[DefineOwnProperty]](P, desc). + bool success = DefineOwnProperty(thread, obj, key, desc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + // 4. If success is false, throw a TypeError exception. + if (!success) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", false); + } + return success; +} + +bool JSTaggedValue::DefineOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc) +{ + if (obj->IsJSArray()) { + return JSArray::DefineOwnProperty(thread, JSHandle(obj), key, desc); + } + + if (obj->IsJSProxy()) { + return JSProxy::DefineOwnProperty(thread, JSHandle(obj), key, desc); + } + + if (obj->IsTypedArray()) { + return JSTypedArray::DefineOwnProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), desc); + } + + return JSObject::DefineOwnProperty(thread, JSHandle(obj), key, desc); +} + +bool JSTaggedValue::GetOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc) +{ + if (obj->IsJSProxy()) { + return JSProxy::GetOwnProperty(thread, JSHandle(obj), key, desc); + } + if (obj->IsTypedArray()) { + return JSTypedArray::GetOwnProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), desc); + } + return JSObject::GetOwnProperty(thread, JSHandle(obj), key, desc); +} + +bool JSTaggedValue::SetPrototype(JSThread *thread, const JSHandle &obj, + const JSHandle &proto) +{ + if (obj->IsJSProxy()) { + return JSProxy::SetPrototype(thread, JSHandle(obj), proto); + } + + return JSObject::SetPrototype(thread, JSHandle(obj), proto); +} + +bool JSTaggedValue::PreventExtensions(JSThread *thread, const JSHandle &obj) +{ + if (obj->IsJSProxy()) { + return JSProxy::PreventExtensions(thread, JSHandle(obj)); + } + return JSObject::PreventExtensions(thread, JSHandle(obj)); +} + +JSHandle JSTaggedValue::GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj) +{ + if (obj->IsJSProxy()) { + return JSProxy::OwnPropertyKeys(thread, JSHandle(obj)); + } + if (obj->IsTypedArray()) { + return JSTypedArray::OwnPropertyKeys(thread, obj); + } + return JSObject::GetOwnPropertyKeys(thread, JSHandle(obj)); +} + +// 7.3.10 HasProperty (O, P) +bool JSTaggedValue::HasProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + if (obj->IsJSProxy()) { + return JSProxy::HasProperty(thread, JSHandle(obj), key); + } + if (obj->IsTypedArray()) { + return JSTypedArray::HasProperty(thread, obj, JSTypedArray::ToPropKey(thread, key)); + } + return JSObject::HasProperty(thread, JSHandle(obj), key); +} + +bool JSTaggedValue::HasProperty(JSThread *thread, const JSHandle &obj, uint32_t key) +{ + if (obj->IsJSProxy()) { + JSHandle keyHandle(thread, JSTaggedValue(key)); + return JSProxy::HasProperty(thread, JSHandle(obj), keyHandle); + } + if (obj->IsTypedArray()) { + JSHandle key_handle(thread, JSTaggedValue(key)); + return JSTypedArray::HasProperty(thread, obj, JSHandle(ToString(thread, key_handle))); + } + return JSObject::HasProperty(thread, JSHandle(obj), key); +} + +// 7.3.11 HasOwnProperty (O, P) +bool JSTaggedValue::HasOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + PropertyDescriptor desc(thread); + return JSTaggedValue::GetOwnProperty(thread, obj, key, desc); +} + +bool JSTaggedValue::GlobalHasOwnProperty(JSThread *thread, const JSHandle &key) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + PropertyDescriptor desc(thread); + return JSObject::GlobalGetOwnProperty(thread, key, desc); +} + +JSTaggedNumber JSTaggedValue::ToIndex(JSThread *thread, const JSHandle &tagged) +{ + if (tagged->IsUndefined()) { + return JSTaggedNumber(0); + } + JSTaggedNumber integerIndex = ToInteger(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedNumber::Exception()); + if (integerIndex.GetNumber() < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "integerIndex < 0", JSTaggedNumber::Exception()); + } + JSTaggedNumber index = ToLength(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedNumber::Exception()); + if (!SameValue(integerIndex, index)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "integerIndex != index", JSTaggedNumber::Exception()); + } + return index; +} + +JSHandle JSTaggedValue::ToPrototypeOrObj(JSThread *thread, const JSHandle &obj) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + if (obj->IsNumber()) { + return JSHandle(thread, + env->GetNumberFunction().GetObject()->GetFunctionPrototype()); + } + if (obj->IsBoolean()) { + return JSHandle(thread, + env->GetBooleanFunction().GetObject()->GetFunctionPrototype()); + } + if (obj->IsString()) { + return JSHandle(thread, + env->GetStringFunction().GetObject()->GetFunctionPrototype()); + } + if (obj->IsSymbol()) { + return JSHandle(thread, + env->GetSymbolFunction().GetObject()->GetFunctionPrototype()); + } + + return obj; +} + +JSTaggedValue JSTaggedValue::GetSuperBase(JSThread *thread, const JSHandle &obj) +{ + if (obj->IsUndefined()) { + return JSTaggedValue::Undefined(); + } + + ASSERT(obj->IsECMAObject()); + return JSObject::Cast(obj.GetTaggedValue())->GetPrototype(thread); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_tagged_value.h b/ecmascript/js_tagged_value.h new file mode 100644 index 0000000000000000000000000000000000000000..c904f17da0d2a77ac2ad368a1fd5916bbaaa11de --- /dev/null +++ b/ecmascript/js_tagged_value.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TAGGED_VALUE_H +#define PANDA_RUNTIME_ECMASCRIPT_TAGGED_VALUE_H + +#include "ecmascript/mem/c_string.h" +#include "include/coretypes/tagged_value.h" + +namespace panda::ecmascript { +class JSObject; +class JSTaggedNumber; +template +class JSHandle; +class TaggedArray; +class LinkedHashMap; +class LinkedHashSet; +class PropertyDescriptor; +class OperationResult; +class EcmaString; +class JSThread; + +// Don't switch the order! +enum PreferredPrimitiveType : uint8_t { PREFER_NUMBER = 0, PREFER_STRING, NO_PREFERENCE }; + +// Result of an abstract relational comparison of x and y, implemented according +// to ES6 section 7.2.11 Abstract Relational Comparison. +enum class ComparisonResult { + LESS, // x < y + EQUAL, // x = y + GREAT, // x > y + UNDEFINED // at least one of x or y was undefined or NaN +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_TAGGED_VALUE_IF_ABRUPT(thread) RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Undefined()); + +using JSTaggedType = coretypes::TaggedType; + +static inline JSTaggedType ReinterpretDoubleToTaggedType(double value) +{ + return bit_cast(value); +} +static inline double ReinterpretTaggedTypeToDouble(JSTaggedType value) +{ + return bit_cast(value); +} + +class JSTaggedValue : public coretypes::TaggedValue { +public: + static JSTaggedValue Cast(ObjectHeader *object) + { + return JSTaggedValue(object); + } + + JSTaggedValue(void *) = delete; + + constexpr JSTaggedValue() = default; + constexpr explicit JSTaggedValue(coretypes::TaggedType v) : coretypes::TaggedValue(v) {} + constexpr explicit JSTaggedValue(int v) : coretypes::TaggedValue(v) {} + explicit JSTaggedValue(unsigned int v) : coretypes::TaggedValue(v) {} + constexpr explicit JSTaggedValue(bool v) : coretypes::TaggedValue(v) {} + explicit JSTaggedValue(double v) : coretypes::TaggedValue(v) {} + explicit JSTaggedValue(const ObjectHeader *v) : coretypes::TaggedValue(v) {} + explicit JSTaggedValue(const TaggedObject *v) : coretypes::TaggedValue(v) {} + explicit JSTaggedValue(const coretypes::TaggedValue &other) : coretypes::TaggedValue(other.GetRawData()) {} + explicit JSTaggedValue(int64_t v) : coretypes::TaggedValue(v){}; + + ~JSTaggedValue() = default; + DEFAULT_COPY_SEMANTIC(JSTaggedValue); + DEFAULT_MOVE_SEMANTIC(JSTaggedValue); + + inline TaggedObject *GetWeakReferentUnChecked() const + { + return reinterpret_cast(GetRawData() & (~TAG_WEAK_MASK)); + } + + static inline constexpr JSTaggedValue False() + { + return JSTaggedValue(VALUE_FALSE); + } + + static inline constexpr JSTaggedValue True() + { + return JSTaggedValue(VALUE_TRUE); + } + + static inline constexpr JSTaggedValue Undefined() + { + return JSTaggedValue(VALUE_UNDEFINED); + } + + static inline constexpr JSTaggedValue Null() + { + return JSTaggedValue(VALUE_NULL); + } + + static inline constexpr JSTaggedValue Hole() + { + return JSTaggedValue(VALUE_HOLE); + } + + static inline constexpr JSTaggedValue Exception() + { + return JSTaggedValue(VALUE_EXCEPTION); + } + + inline double GetNumber() const + { + return IsInt() ? GetInt() : GetDouble(); + } + + inline TaggedObject *GetTaggedObject() const + { + return reinterpret_cast(GetHeapObject()); + } + + inline TaggedObject *GetRawTaggedObject() const + { + return reinterpret_cast(GetRawHeapObject()); + } + + inline TaggedObject *GetTaggedWeakRef() const + { + return reinterpret_cast(GetWeakReferent()); + } + + static JSTaggedValue OrdinaryToPrimitive(JSThread *thread, const JSHandle &tagged, + PreferredPrimitiveType type = PREFER_NUMBER); + + // ecma6 7.1 Type Conversion + static JSTaggedValue ToPrimitive(JSThread *thread, const JSHandle &tagged, + PreferredPrimitiveType type = NO_PREFERENCE); + bool ToBoolean() const; + static JSTaggedNumber ToNumber(JSThread *thread, const JSHandle &tagged); + static JSTaggedNumber ToInteger(JSThread *thread, const JSHandle &tagged); + static int32_t ToInt32(JSThread *thread, const JSHandle &tagged); + static uint32_t ToUint32(JSThread *thread, const JSHandle &tagged); + static int16_t ToInt16(JSThread *thread, const JSHandle &tagged); + static uint16_t ToUint16(JSThread *thread, const JSHandle &tagged); + static int8_t ToInt8(JSThread *thread, const JSHandle &tagged); + static uint8_t ToUint8(JSThread *thread, const JSHandle &tagged); + static uint8_t ToUint8Clamp(JSThread *thread, const JSHandle &tagged); + static JSHandle ToString(JSThread *thread, const JSHandle &tagged); + static JSHandle ToObject(JSThread *thread, const JSHandle &tagged); + static JSHandle ToPropertyKey(JSThread *thread, const JSHandle &tagged); + static JSTaggedNumber ToLength(JSThread *thread, const JSHandle &tagged); + static JSTaggedValue CanonicalNumericIndexString(JSThread *thread, const JSHandle &tagged); + static JSTaggedNumber ToIndex(JSThread *thread, const JSHandle &tagged); + + static bool ToArrayLength(JSThread *thread, const JSHandle &tagged, uint32_t *output); + static bool ToElementIndex(JSTaggedValue key, uint32_t *output); + static bool StringToElementIndex(JSTaggedValue key, uint32_t *output); + array_size_t GetArrayLength() const; + + // ecma6 7.2 Testing and Comparison Operations + bool IsCallable() const; + bool IsConstructor() const; + bool IsExtensible(JSThread *thread) const; + bool IsInteger() const; + bool WithinInt32() const; + bool IsZero() const; + static bool IsPropertyKey(const JSHandle &key); + static JSHandle RequireObjectCoercible(JSThread *thread, const JSHandle &tagged); + static bool SameValue(const JSTaggedValue &x, const JSTaggedValue &y); + static bool SameValue(const JSHandle &xHandle, const JSHandle &yHandle); + static bool SameValueZero(const JSTaggedValue &x, const JSTaggedValue &y); + static bool Less(JSThread *thread, const JSHandle &x, const JSHandle &y); + static bool Equal(JSThread *thread, const JSHandle &x, const JSHandle &y); + static bool StrictEqual(const JSThread *thread, const JSHandle &x, const JSHandle &y); + static bool SameValueNumberic(const JSTaggedValue &x, const JSTaggedValue &y); + + // ES6 7.4 Operations on Iterator Objects + static JSObject *CreateIterResultObject(JSThread *thread, const JSHandle &value, bool done); + + // ecma6 7.3 + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, uint32_t key); + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &receiver); + static bool SetProperty(JSThread *thread, const JSHandle &obj, uint32_t key, + const JSHandle &value, bool mayThrow = false); + + static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, bool mayThrow = false); + + static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver, + bool mayThrow = false); + static bool DeleteProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + static bool DeletePropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + static bool DefinePropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc); + static bool DefineOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc); + static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + PropertyDescriptor &desc); + static bool SetPrototype(JSThread *thread, const JSHandle &obj, + const JSHandle &proto); + static bool PreventExtensions(JSThread *thread, const JSHandle &obj); + static JSHandle GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj); + static bool HasProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); + static bool HasProperty(JSThread *thread, const JSHandle &obj, uint32_t key); + static bool HasOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + static bool GlobalHasOwnProperty(JSThread *thread, const JSHandle &key); + + // Type + bool IsJSMap() const; + bool IsJSSet() const; + bool IsJSWeakMap() const; + bool IsJSWeakSet() const; + bool IsJSRegExp() const; + bool IsNumber() const; + bool IsString() const; + bool IsStringOrSymbol() const; + bool IsTaggedArray() const; + bool IsNativePointer() const; + bool IsJSNativePointer() const; + bool IsJSNativeObject() const; + bool IsBoolean() const; + bool IsSymbol() const; + bool IsJSObject() const; + bool IsJSGlobalObject() const; + bool IsJSError() const; + bool IsArray(JSThread *thread) const; + bool IsJSArray() const; + bool IsStableJSArray(JSThread *thread) const; + bool IsTypedArray() const; + bool IsJSTypedArray() const; + bool IsJSInt8Array() const; + bool IsJSUint8Array() const; + bool IsJSUint8ClampedArray() const; + bool IsJSInt16Array() const; + bool IsJSUint16Array() const; + bool IsJSInt32Array() const; + bool IsJSUint32Array() const; + bool IsJSFloat32Array() const; + bool IsJSFloat64Array() const; + bool IsArguments() const; + bool IsDate() const; + bool IsBoundFunction() const; + bool IsProxyRevocFunction() const; + bool IsJSAsyncFunction() const; + bool IsJSAsyncAwaitStatusFunction() const; + bool IsClassConstructor() const; + bool IsClassPrototype() const; + bool IsJSFunction() const; + bool IsJSFunctionBase() const; + bool IsECMAObject() const; + bool IsJSPrimitiveRef() const; + bool IsJSPrimitive() const; + bool IsAccessorData() const; + bool IsInternalAccessor() const; + bool IsAccessor() const; + bool IsJSGlobalEnv() const; + bool IsJSProxy() const; + bool IsDynClass() const; + bool IsJSHClass() const; + bool IsForinIterator() const; + bool IsObjectWrapper() const; + bool IsStringIterator() const; + bool IsArrayBuffer() const; + + bool IsJSSetIterator() const; + bool IsJSMapIterator() const; + bool IsJSArrayIterator() const; + bool IsIterator() const; + bool IsGeneratorFunction() const; + bool IsGeneratorObject() const; + bool IsAsyncFuncObject() const; + bool IsJSPromise() const; + bool IsRecord() const; + bool IsPromiseReaction() const; + bool IsProgram() const; + bool IsLexicalFunction() const; + bool IsJSPromiseReactionFunction() const; + bool IsJSPromiseExecutorFunction() const; + bool IsJSPromiseAllResolveElementFunction() const; + bool IsPromiseCapability() const; + bool IsPromiseIteratorRecord() const; + bool IsPromiseRecord() const; + bool IsResolvingFunctionsRecord() const; + bool IsCompletionRecord() const; + bool IsDataView() const; + bool IsTemplateMap() const; + bool IsJSFunctionExtraInfo() const; + bool IsMicroJobQueue() const; + bool IsPendingJob() const; + + bool IsPrototypeHandler() const; + bool IsTransitionHandler() const; + bool IsPropertyBox() const; + bool IsProtoChangeMarker() const; + bool IsProtoChangeDetails() const; + static bool IsSameTypeOrHClass(JSTaggedValue x, JSTaggedValue y); + + static ComparisonResult Compare(JSThread *thread, const JSHandle &x, + const JSHandle &y); + static ComparisonResult StrictNumberCompare(double x, double y); + static bool StrictNumberEquals(double x, double y); + + static JSHandle ToPrototypeOrObj(JSThread *thread, const JSHandle &obj); + inline uint32_t GetKeyHashCode() const; + static JSTaggedValue GetSuperBase(JSThread *thread, const JSHandle &obj); + + void DumpTaggedValue(JSThread *thread, std::ostream &os) const DUMP_API_ATTR; + void Dump(JSThread *thread, std::ostream &os) const DUMP_API_ATTR; + void Dump(JSThread *thread) const DUMP_API_ATTR; + void DumpForSnapshot(JSThread *thread, std::vector> &vec) const; + static void DumpVal(JSThread *thread, JSTaggedType val) DUMP_API_ATTR; + +private: + inline double ExtractNumber() const; + + void DumpSpecialValue([[maybe_unused]] JSThread *thread, std::ostream &os) const; + void DumpHeapObjectType([[maybe_unused]] JSThread *thread, std::ostream &os) const; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_TAGGED_VALUE_H diff --git a/ecmascript/js_thread.cpp b/ecmascript/js_thread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a8ab74bdb1bb993f0299fb77f2dd0e45d809b54a --- /dev/null +++ b/ecmascript/js_thread.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/global_env_constants-inl.h" +#include "ecmascript/interpreter/interpreter-inl.h" +#include "ecmascript/js_thread.h" +#include "include/panda_vm.h" + +namespace panda::ecmascript { +// static +JSThread *JSThread::Create(Runtime *runtime, PandaVM *vm) +{ + auto jsThread = new JSThread(runtime, vm); + jsThread->regionFactory_ = EcmaVM::Cast(vm)->GetRegionFactory(); + // algin with 16 + jsThread->frameBase_ = static_cast( + EcmaVM::Cast(vm)->GetRegionFactory()->Allocate(sizeof(JSTaggedType) * MAX_STACK_SIZE)); + jsThread->currentFrame_ = jsThread->frameBase_ + MAX_STACK_SIZE; + JSThread::SetCurrent(jsThread); + EcmaInterpreter::InitStackFrame(jsThread); + + return jsThread; +} + +JSThread::JSThread(Runtime *runtime, PandaVM *vm) + : ManagedThread(NON_INITIALIZED_THREAD_ID, runtime->GetInternalAllocator(), vm, + Thread::ThreadType::THREAD_TYPE_MANAGED) +{ + SetLanguageContext(runtime->GetLanguageContext(panda_file::SourceLang::ECMASCRIPT)); + globalHandleStorage_ = + EcmaVM::Cast(vm)->GetChunk()->New>(runtime->GetInternalAllocator()); +} + +JSThread::~JSThread() +{ + for (auto n : handleStorageNodes_) { + delete n; + } + handleStorageNodes_.clear(); + currentHandleStorageIndex_ = -1; + handleScopeStorageNext_ = handleScopeStorageEnd_ = nullptr; + EcmaVM::Cast(GetVM())->GetChunk()->Delete(globalHandleStorage_); + + GetRegionFactory()->Free(frameBase_, sizeof(JSTaggedType) * MAX_STACK_SIZE); + frameBase_ = nullptr; + regionFactory_ = nullptr; +} + +EcmaVM *JSThread::GetEcmaVM() const +{ + return EcmaVM::Cast(GetVM()); +} + +void JSThread::SetException(JSTaggedValue exception) +{ + exception_ = exception; +} + +void JSThread::ClearException() +{ + exception_ = JSTaggedValue::Hole(); +} + +JSTaggedValue JSThread::GetCurrentLexenv() const +{ + return EcmaFrameHandler(currentFrame_).GetEnv(); +} + +void JSThread::Iterate(const RootVisitor &v0, const RootRangeVisitor &v1) +{ + if (!exception_.IsHole()) { + v0(Root::ROOT_VM, ObjectSlot(ToUintPtr(&exception_))); + } + + // visit global Constant + globalConst_.Visitor(v1); + // visit stack roots + EcmaFrameHandler(currentFrame_).Iterate(v0, v1); + + // visit tagged handle storage roots + if (currentHandleStorageIndex_ != -1) { + int32_t nid = currentHandleStorageIndex_; + for (int32_t i = 0; i <= nid; ++i) { + auto node = handleStorageNodes_.at(i); + auto start = node->data(); + auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_; + v1(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end))); + } + } + // visit global handle storage roots + if (globalHandleStorage_->GetNodes()->empty()) { + return; + } + for (size_t i = 0; i < globalHandleStorage_->GetNodes()->size() - 1; i++) { + auto block = globalHandleStorage_->GetNodes()->at(i); + auto size = GlobalHandleStorage::GLOBAL_BLOCK_SIZE; + + for (auto j = 0; j < size; j++) { + JSTaggedValue value(block->at(j).GetObject()); + if (value.IsHeapObject()) { + v0(ecmascript::Root::ROOT_HANDLE, ecmascript::ObjectSlot(block->at(j).GetObjectAddress())); + } + } + } + + auto block = globalHandleStorage_->GetNodes()->back(); + auto size = globalHandleStorage_->GetCount(); + for (auto j = 0; j < size; j++) { + JSTaggedValue value(block->at(j).GetObject()); + if (value.IsHeapObject()) { + v0(ecmascript::Root::ROOT_HANDLE, ecmascript::ObjectSlot(block->at(j).GetObjectAddress())); + } + } +} + +bool JSThread::DoStackOverflowCheck(const JSTaggedType *sp) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (UNLIKELY(sp <= frameBase_ + RESERVE_STACK_SIZE)) { + ObjectFactory *factory = GetEcmaVM()->GetFactory(); + JSHandle error = factory->GetJSError(base::ErrorType::RANGE_ERROR, "Stack overflow!"); + if (LIKELY(!HasPendingException())) { + SetException(error.GetTaggedValue()); + } + return true; + } + return false; +} + +uintptr_t *JSThread::ExpandHandleStorage() +{ + uintptr_t *result = nullptr; + int32_t lastIndex = handleStorageNodes_.size() - 1; + if (currentHandleStorageIndex_ == lastIndex) { + auto n = new std::array(); + handleStorageNodes_.push_back(n); + currentHandleStorageIndex_++; + result = reinterpret_cast(&n->data()[0]); + handleScopeStorageEnd_ = &n->data()[NODE_BLOCK_SIZE]; + } else { + currentHandleStorageIndex_++; + auto lastNode = handleStorageNodes_[currentHandleStorageIndex_]; + result = reinterpret_cast(&lastNode->data()[0]); + handleScopeStorageEnd_ = &lastNode->data()[NODE_BLOCK_SIZE]; + } + + return result; +} + +void JSThread::ShrunkHandleStorage(const JSTaggedType *end) +{ + for (int32_t i = currentHandleStorageIndex_; i >= 0; i--) { + auto node = handleStorageNodes_[i]; + auto currentEnd = &node->data()[NODE_BLOCK_SIZE]; + if (end == currentEnd) { + currentHandleStorageIndex_ = i; + return; + } + } + ASSERT(end == nullptr); + currentHandleStorageIndex_ = -1; +} + +void JSThread::NotifyStableArrayElementsGuardians(JSHandle receiver) +{ + if (!receiver->GetJSHClass()->IsPrototype()) { + return; + } + if (!stableArrayElementsGuardians_) { + return; + } + auto env = GetEcmaVM()->GetGlobalEnv(); + if (receiver.GetTaggedValue() == env->GetObjectFunctionPrototype().GetTaggedValue() || + receiver.GetTaggedValue() == env->GetArrayPrototype().GetTaggedValue()) { + stableArrayElementsGuardians_ = false; + } +} + +void JSThread::ResetGuardians() +{ + stableArrayElementsGuardians_ = true; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_thread.h b/ecmascript/js_thread.h new file mode 100644 index 0000000000000000000000000000000000000000..7b45bb236879cede3a41a558cb6d694965c39918 --- /dev/null +++ b/ecmascript/js_thread.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_THREAD_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_THREAD_H + +#include "ecmascript/global_env_constants.h" +#include "ecmascript/interpreter/frame_handler.h" +#include "ecmascript/mem/heap_roots.h" +#include "global_handle_storage-inl.h" +#include "include/thread.h" + +namespace panda::ecmascript { +class EcmaVM; +class RegionFactory; + +class JSThread : public ManagedThread { +public: + static JSThread *Cast(ManagedThread *thread) + { + ASSERT(thread != nullptr); + return reinterpret_cast(thread); + } + + JSThread(Runtime *runtime, PandaVM *vm); + + ~JSThread() override; + + EcmaVM *GetEcmaVM() const; + + static JSThread *Create(Runtime *runtime, PandaVM *vm); + + int GetNestedLevel() const + { + return nestedLevel_; + } + + void SetNestedLevel(int level) + { + nestedLevel_ = level; + } + + bool IsSnapshotMode() const + { + return isSnapshotMode_; + } + + void SetIsSnapshotMode(bool value) + { + isSnapshotMode_ = value; + } + + JSTaggedType *GetCurrentSPFrame() const + { + return currentFrame_; + } + + void SetCurrentSPFrame(JSTaggedType *sp) + { + currentFrame_ = sp; + } + + bool DoStackOverflowCheck(const JSTaggedType *sp); + + bool IsEcmaInterpreter() const + { + return isEcmaInterpreter_; + } + + void SetIsEcmaInterpreter(bool value) + { + isEcmaInterpreter_ = value; + } + + RegionFactory *GetRegionFactory() const + { + return regionFactory_; + } + + void Iterate(const RootVisitor &v0, const RootRangeVisitor &v1); + + uintptr_t *ExpandHandleStorage(); + void ShrunkHandleStorage(const JSTaggedType *end); + + JSTaggedType *GetHandleScopeStorageNext() const + { + return handleScopeStorageNext_; + } + + void SetHandleScopeStorageNext(JSTaggedType *value) + { + handleScopeStorageNext_ = value; + } + + JSTaggedType *GetHandleScopeStorageEnd() const + { + return handleScopeStorageEnd_; + } + + void SetHandleScopeStorageEnd(JSTaggedType *value) + { + handleScopeStorageEnd_ = value; + } + + void SetException(JSTaggedValue exception); + + JSTaggedValue GetException() const + { + return exception_; + } + + bool HasPendingException() const + { + return !exception_.IsHole(); + } + + void ClearException(); + + GlobalHandleStorage *GetGlobalHandleStorage() const + { + return globalHandleStorage_; + } + + const GlobalEnvConstants *GlobalConstants() const + { + return &globalConst_; + } + + void NotifyStableArrayElementsGuardians(JSHandle receiver); + + bool IsStableArrayElementsGuardiansInvalid() const + { + return !stableArrayElementsGuardians_; + } + + void ResetGuardians(); + + JSTaggedValue GetCurrentLexenv() const; + +private: + NO_COPY_SEMANTIC(JSThread); + NO_MOVE_SEMANTIC(JSThread); + + void DumpStack() DUMP_API_ATTR; + + static constexpr uint32_t MAX_STACK_SIZE = 256 * 1024; + static constexpr uint32_t RESERVE_STACK_SIZE = 128; + static const uint32_t NODE_BLOCK_SIZE_LOG2 = 10; + static const uint32_t NODE_BLOCK_SIZE = 1U << NODE_BLOCK_SIZE_LOG2; + + GlobalHandleStorage *globalHandleStorage_{nullptr}; + + os::memory::ConditionVariable initializationVar_ GUARDED_BY(initializationLock_); + os::memory::Mutex initializationLock_; + int nestedLevel_ = 0; + JSTaggedType *currentFrame_{nullptr}; + JSTaggedType *frameBase_{nullptr}; + bool isSnapshotMode_{false}; + bool isEcmaInterpreter_{false}; + RegionFactory *regionFactory_{nullptr}; + JSTaggedType *handleScopeStorageNext_{nullptr}; + JSTaggedType *handleScopeStorageEnd_{nullptr}; + std::vector *> handleStorageNodes_{}; + int32_t currentHandleStorageIndex_{-1}; + JSTaggedValue exception_{JSTaggedValue::Hole()}; + bool stableArrayElementsGuardians_{true}; + GlobalEnvConstants globalConst_; // Place-Holder + + friend class EcmaHandleScope; + friend class GlobalHandleCollection; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_THREAD_H diff --git a/ecmascript/js_typed_array.cpp b/ecmascript/js_typed_array.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fb9c4f9634ee51cd45cae835eb7220ee8717ba75 --- /dev/null +++ b/ecmascript/js_typed_array.cpp @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_typed_array.h" +#include "ecmascript/accessor_data.h" +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/builtins/builtins_arraybuffer.h" + +namespace panda::ecmascript { +using TypedArrayHelper = base::TypedArrayHelper; +using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; + +JSHandle JSTypedArray::ToPropKey(JSThread *thread, const JSHandle &key) +{ + if (key->IsSymbol()) { + return key; + } + return JSHandle(JSTaggedValue::ToString(thread, key)); +} +// 9.4.5.1 [[GetOwnProperty]] ( P ) +bool JSTypedArray::GetOwnProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, PropertyDescriptor &desc) +{ + // 1. Assert : IsPropertyKey(P) is true. + ASSERT(JSTaggedValue::IsPropertyKey(key)); + // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot. + // 3. If Type(P) is String, then + // a. Let numericIndex be CanonicalNumericIndexString(P). + // b. Assert: numericIndex is not an abrupt completion. + // c. If numericIndex is not undefined, then + // i. Let value be IntegerIndexedElementGet (O, numericIndex). + // ii. ReturnIfAbrupt(value). + // iii. If value is undefined, return undefined. + // iv. Return a PropertyDescriptor{ [[Value]]: value, [[Enumerable]]: true, [[Writable]]: true, + // [[Configurable]]: false }. + if (key->IsString()) { + JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!numericIndex.IsUndefined()) { + JSHandle value = + JSTypedArray::IntegerIndexedElementGet(thread, typedarray, numericIndex).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (value->IsUndefined()) { + return false; + } + desc.SetValue(value); + desc.SetEnumerable(true); + desc.SetWritable(true); + desc.SetConfigurable(true); + return true; + } + } + // 4. Return OrdinaryGetOwnProperty(O, P). + return JSObject::OrdinaryGetOwnProperty(thread, JSHandle(typedarray), key, desc); +} + +// 9.4.5.2 [[HasProperty]] ( P ) +bool JSTypedArray::HasProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key) +{ + // 1. Assert: IsPropertyKey(P) is true. + ASSERT(JSTaggedValue::IsPropertyKey(key)); + // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot. + // 3. If Type(P) is String, then + // a. Let numericIndex be CanonicalNumericIndexString(P). + // b. Assert: numericIndex is not an abrupt completion. + // c. If numericIndex is not undefined, then + // i. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + // ii. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + // iii. If IsInteger(numericIndex) is false, return false + // iv. If numericIndex = −0, return false. + // v. If numericIndex < 0, return false. + // vi. If numericIndex ≥ the value of O’s [[ArrayLength]] internal slot, return false. + // vii. Return true. + JSHandle typedarrayObj(typedarray); + if (key->IsString()) { + JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!numericIndex.IsUndefined()) { + JSTaggedValue buffer = TypedArrayHelper::GetViewedArrayBuffer(typedarrayObj); + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", false); + } + if (!numericIndex.IsInteger()) { + return false; + } + JSHandle numericIndexHandle(thread, numericIndex); + JSTaggedNumber numericIndexNumber = JSTaggedValue::ToNumber(thread, numericIndexHandle); + double tNegZero = -0.0; + auto eZero = JSTaggedNumber(tNegZero); + JSHandle zero(thread, JSTaggedValue(0)); + if (JSTaggedNumber::SameValue(numericIndexNumber, eZero)) { + return false; + } + + if (JSTaggedValue::Less(thread, numericIndexHandle, zero)) { + return false; + } + int32_t arrLen = TypedArrayHelper::GetArrayLength(thread, typedarrayObj); + JSHandle arrLenHandle(thread, JSTaggedValue(arrLen)); + return JSTaggedValue::Less(thread, numericIndexHandle, arrLenHandle); + } + } + // 4. Return OrdinaryHasProperty(O, P). + PropertyDescriptor desc(thread); + if (JSObject::OrdinaryGetOwnProperty(thread, typedarrayObj, key, desc)) { + return true; + } + JSTaggedValue parent = typedarrayObj->GetPrototype(thread); + if (!parent.IsNull()) { + return JSTaggedValue::HasProperty(thread, JSHandle(thread, parent), key); + } + return false; +} + +// 9.4.5.3 [[DefineOwnProperty]] ( P, Desc ) +bool JSTypedArray::DefineOwnProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const PropertyDescriptor &desc) +{ + // 1. Assert: IsPropertyKey(P) is true. + ASSERT(JSTaggedValue::IsPropertyKey(key)); + // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot. + // 3. If Type(P) is String, then + // a. Let numericIndex be CanonicalNumericIndexString (P). + // b. Assert: numericIndex is not an abrupt completion. + // c. If numericIndex is not undefined, then + JSHandle typedarrayObj(typedarray); + if (key->IsString()) { + JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!numericIndex.IsUndefined()) { + // i. If IsInteger(numericIndex) is false, return false + // ii. Let intIndex be numericIndex. + // iii. If intIndex = −0, return false. + // iv. If intIndex < 0, return false. + // v. Let length be the value of O’s [[ArrayLength]] internal slot. + // vi. If intIndex ≥ length, return false. + // vii. If IsAccessorDescriptor(Desc) is true, return false. + // viii. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is true, return false. + // ix. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] is false, return false. + // x. If Desc has a [[Writable]] field and if Desc.[[Writable]] is false, return false. + // xi. If Desc has a [[Value]] field, then + // 1. Let value be Desc.[[Value]]. + // 2. Return IntegerIndexedElementSet (O, intIndex, value). + // xii. Return true. + if (!numericIndex.IsInteger()) { + return false; + } + JSHandle numericIndexHandle(thread, numericIndex); + JSTaggedNumber numericIndexNumber = JSTaggedValue::ToNumber(thread, numericIndexHandle); + double tNegZero = -0.0; + auto eZero = JSTaggedNumber(tNegZero); + JSHandle zero(thread, JSTaggedValue(0)); + if (JSTaggedNumber::SameValue(numericIndexNumber, eZero)) { + return false; + } + if (JSTaggedValue::Less(thread, numericIndexHandle, zero)) { + return false; + } + int32_t arrLen = TypedArrayHelper::GetArrayLength(thread, typedarrayObj); + JSHandle arrLenHandle(thread, JSTaggedValue(arrLen)); + if (!JSTaggedValue::Less(thread, numericIndexHandle, arrLenHandle)) { + return false; + } + if (desc.IsAccessorDescriptor()) { + return false; + } + if (desc.HasConfigurable() && !desc.IsConfigurable()) { + return false; + } + if (desc.HasEnumerable() && !desc.IsEnumerable()) { + return false; + } + if (desc.HasWritable() && !desc.IsWritable()) { + return false; + } + if (desc.HasValue()) { + JSHandle value = desc.GetValue(); + return (JSTypedArray::IntegerIndexedElementSet(thread, typedarray, numericIndex, value)); + } + return true; + } + } + // 4. Return OrdinaryDefineOwnProperty(O, P, Desc). + return JSObject::OrdinaryDefineOwnProperty(thread, typedarrayObj, key, desc); +} + +// 9.4.5.4 [[Get]] ( P, Receiver ) +OperationResult JSTypedArray::GetProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const JSHandle &receiver) +{ + // 1. Assert : IsPropertyKey(P) is true. + ASSERT(JSTaggedValue::IsPropertyKey(key)); + // 2. If Type(P) is String and if SameValue(O, Receiver) is true, then + if (key->IsString() && JSTaggedValue::SameValue(typedarray, receiver)) { + // a. Let numericIndex be CanonicalNumericIndexString (P). + // b. Assert: numericIndex is not an abrupt completion. + // c. If numericIndex is not undefined, then + // i. Return IntegerIndexedElementGet (O, numericIndex). + JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION( + thread, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + if (!numericIndex.IsUndefined()) { + return JSTypedArray::IntegerIndexedElementGet(thread, typedarray, numericIndex); + } + } + + // 3. Return the result of calling the default ordinary object [[Get]] internal method (9.1.8) on O + // passing P and Receiver as arguments. + return JSObject::GetProperty(thread, typedarray, key, receiver); +} + +// 9.4.5.5 [[Set]] ( P, V, Receiver ) +bool JSTypedArray::SetProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const JSHandle &value, + const JSHandle &receiver, bool mayThrow) +{ + // 1. Assert : IsPropertyKey(P) is true. + ASSERT(JSTaggedValue::IsPropertyKey(key)); + // 2. If Type(P) is String and if SameValue(O, Receiver) is true, then + if (key->IsString() && JSTaggedValue::SameValue(typedarray, receiver)) { + // a. Let numericIndex be CanonicalNumericIndexString (P). + // b. Assert: numericIndex is not an abrupt completion. + // c. If numericIndex is not undefined, then + // i. Return IntegerIndexedElementSet (O, numericIndex, V). + JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!numericIndex.IsUndefined()) { + return JSTypedArray::IntegerIndexedElementSet(thread, typedarray, numericIndex, value); + } + } + // 3. Return the result of calling the default ordinary object [[Set]] internal method (9.1.8) on O passing + // P, V, and Receiver as arguments. + return JSObject::SetProperty(thread, typedarray, key, value, receiver, mayThrow); +} + +// 9.4.5.6 [[OwnPropertyKeys]] ( ) +JSHandle JSTypedArray::OwnPropertyKeys(JSThread *thread, const JSHandle &typedarray) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let keys be a new empty List. + // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and + // [[TypedArrayName]] internal slots. + // 3. Let len be the value of O’s [[ArrayLength]] internal slot. + JSHandle arrayObj(typedarray); + JSHandle objKeys = JSObject::GetOwnPropertyKeys(thread, arrayObj); + array_size_t objKeysLen = objKeys->GetLength(); + array_size_t bufferKeysLen = TypedArrayHelper::GetArrayLength(thread, arrayObj); + array_size_t length = objKeysLen + bufferKeysLen; + JSHandle nameList = factory->NewTaggedArray(length); + + // 4. For each integer i starting with 0 such that i < len, in ascending order, + // a. Add ToString(i) as the last element of keys. + array_size_t copyLength = 0; + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + for (array_size_t k = 0; k < bufferKeysLen; k++) { + tKey.Update(JSTaggedValue(k)); + JSHandle sKey(JSTaggedValue::ToString(thread, tKey)); + nameList->Set(thread, copyLength, sKey.GetTaggedValue()); + copyLength++; + } + + // 5. For each own property key P of O such that Type(P) is String and P is not an integer index, in + // property creation order + // a. Add P as the last element of keys. + for (array_size_t i = 0; i < objKeysLen; i++) { + JSTaggedValue key = objKeys->Get(i); + if (JSTaggedValue(key).IsString()) { + nameList->Set(thread, copyLength, key); + copyLength++; + } + } + + // 6. For each own property key P of O such that Type(P) is Symbol, in property creation order + // a. Add P as the last element of keys. + for (array_size_t i = 0; i < objKeysLen; i++) { + JSTaggedValue key = objKeys->Get(i); + if (JSTaggedValue(key).IsSymbol()) { + nameList->Set(thread, copyLength, key); + copyLength++; + } + } + + // 7. Return keys. + return factory->CopyArray(nameList, length, copyLength); +} + +// 9.4.5.7 IntegerIndexedObjectCreate (prototype, internalSlotsList) + +// 9.4.5.8 IntegerIndexedElementGet ( O, index ) +OperationResult JSTypedArray::IntegerIndexedElementGet(JSThread *thread, const JSHandle &typedarray, + JSTaggedValue index) +{ + // 1. Assert: Type(index) is Number. + ASSERT(index.IsNumber()); + // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and + // [[TypedArrayName]] internal slots. + ASSERT(typedarray->IsTypedArray()); + // 3. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSHandle typedarrayObj(typedarray); + JSTaggedValue buffer = TypedArrayHelper::GetViewedArrayBuffer(typedarrayObj); + // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + // 5. If IsInteger(index) is false, return undefined + if (!index.IsInteger()) { + return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true)); + } + + // 6. If index = −0, return undefined. + // 7. Let length be the value of O’s [[ArrayLength]] internal slot. + // 8. If index < 0 or index ≥ length, return undefined. + JSHandle indexHandle(thread, index); + JSTaggedNumber indexNumber = JSTaggedValue::ToNumber(thread, indexHandle); + double tNegZero = -0.0; + auto eZero = JSTaggedNumber(tNegZero); + JSHandle zero(thread, JSTaggedValue(0)); + if (JSTaggedNumber::SameValue(indexNumber, eZero)) { + return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true)); + } + int32_t arrLen = TypedArrayHelper::GetArrayLength(thread, typedarrayObj); + JSHandle arrLenHandle(thread, JSTaggedValue(arrLen)); + if (JSTaggedValue::Less(thread, indexHandle, zero) || !JSTaggedValue::Less(thread, indexHandle, arrLenHandle)) { + return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true)); + } + // 9. Let offset be the value of O’s [[ByteOffset]] internal slot. + int32_t offset = TypedArrayHelper::GetByteOffset(thread, typedarrayObj); + // 10. Let arrayTypeName be the String value of O’s [[TypedArrayName]] internal slot. + // 11. Let elementSize be the Number value of the Element Size value specified in Table 49 for + // arrayTypeName. + int32_t elementSize = TypedArrayHelper::GetElementSize(typedarrayObj); + // 12. Let indexedPosition = (index × elementSize) + offset. + int32_t k = JSTaggedValue::ToInteger(thread, indexHandle).ToInt32(); + int32_t byteIndex = k * elementSize + offset; + // 13. Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName. + DataViewType elementType = TypedArrayHelper::GetType(typedarrayObj); + // 14. Return GetValueFromBuffer(buffer, indexedPosition, elementType). + JSTaggedValue result = BuiltinsArrayBuffer::GetValueFromBuffer(buffer, byteIndex, elementType, true); + return OperationResult(thread, result, PropertyMetaData(true)); +} + +// static +OperationResult JSTypedArray::FastElementGet(JSThread *thread, const JSHandle &typedarray, + uint32_t index) +{ + // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and + // [[TypedArrayName]] internal slots. + ASSERT(typedarray->IsTypedArray()); + // 3. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSHandle typedarrayObj(typedarray); + JSTaggedValue buffer = TypedArrayHelper::GetViewedArrayBuffer(typedarrayObj); + // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + + // 7. Let length be the value of O’s [[ArrayLength]] internal slot. + // 8. If index < 0 or index ≥ length, return undefined. + int32_t arrLen = TypedArrayHelper::GetArrayLength(thread, typedarrayObj); + if (arrLen < 0 || index >= static_cast(arrLen)) { + return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true)); + } + // 9. Let offset be the value of O’s [[ByteOffset]] internal slot. + int32_t offset = TypedArrayHelper::GetByteOffset(thread, typedarrayObj); + // 11. Let elementSize be the Number value of the Element Size value specified in Table 49 for arrayTypeName. + int32_t elementSize = TypedArrayHelper::GetElementSize(typedarrayObj); + // 12. Let indexedPosition = (index × elementSize) + offset. + int32_t byteIndex = index * elementSize + offset; + // 13. Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName. + DataViewType elementType = TypedArrayHelper::GetType(typedarrayObj); + // 14. Return GetValueFromBuffer(buffer, indexedPosition, elementType). + JSTaggedValue result = BuiltinsArrayBuffer::GetValueFromBuffer(buffer, byteIndex, elementType, true); + return OperationResult(thread, result, PropertyMetaData(true)); +} + +// 9.4.5.9 IntegerIndexedElementSet ( O, index, value ) +bool JSTypedArray::IntegerIndexedElementSet(JSThread *thread, const JSHandle &typedarray, + JSTaggedValue index, const JSHandle &value) +{ + // 1. Assert: Type(index) is Number. + ASSERT(index.IsNumber()); + // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and + // [[TypedArrayName]] internal slots. + ASSERT(typedarray->IsTypedArray()); + // 3. Let numValue be ToNumber(value). + JSTaggedNumber numVal = JSTaggedValue::ToNumber(thread, value); + // 4. ReturnIfAbrupt(numValue). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 5. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSHandle typedarrayObj(typedarray); + JSTaggedValue buffer = TypedArrayHelper::GetViewedArrayBuffer(typedarrayObj); + // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", false); + } + // 7. If IsInteger(index) is false, return false + if (!index.IsInteger()) { + return false; + } + + // 8. If index = −0, return false. + // 9. Let length be the value of O’s [[ArrayLength]] internal slot. + // 10. If index < 0 or index ≥ length, return false. + JSHandle indexHandle(thread, index); + JSTaggedNumber indexNumber = JSTaggedValue::ToNumber(thread, indexHandle); + double tNegZero = -0.0; + auto eZero = JSTaggedNumber(tNegZero); + JSHandle zero(thread, JSTaggedValue(0)); + if (JSTaggedNumber::SameValue(indexNumber, eZero)) { + return false; + } + int32_t arrLen = TypedArrayHelper::GetArrayLength(thread, typedarrayObj); + JSHandle arrLenHandle(thread, JSTaggedValue(arrLen)); + if (JSTaggedValue::Less(thread, indexHandle, zero) || !JSTaggedValue::Less(thread, indexHandle, arrLenHandle)) { + return false; + } + + // 11. Let offset be the value of O’s [[ByteOffset]] internal slot. + int32_t offset = TypedArrayHelper::GetByteOffset(thread, typedarrayObj); + // 12. Let arrayTypeName be the String value of O’s [[TypedArrayName]] internal slot. + // 13. Let elementSize be the Number value of the Element Size value specified in Table 49 for + // arrayTypeName. + int32_t elementSize = TypedArrayHelper::GetElementSize(typedarrayObj); + // 14. Let indexedPosition = (index × elementSize) + offset. + int32_t k = JSTaggedValue::ToInteger(thread, indexHandle).ToInt32(); + int32_t byteIndex = k * elementSize + offset; + // 15. Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName. + DataViewType elementType = TypedArrayHelper::GetType(typedarrayObj); + // 16. Perform SetValueInBuffer(buffer, indexedPosition, elementType, numValue). + BuiltinsArrayBuffer::SetValueInBuffer(buffer, byteIndex, elementType, numVal, true); + // 17. Return true. + return true; +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_typed_array.h b/ecmascript/js_typed_array.h new file mode 100644 index 0000000000000000000000000000000000000000..7ddd78a91d24f8e495db628fe6aecf094011684b --- /dev/null +++ b/ecmascript/js_typed_array.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_TYPED_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_TYPED_ARRAY_H + +#include "ecmascript/tagged_array.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSTypedArray : public JSObject { +public: + static JSTypedArray *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSTypedArray()); + return static_cast(object); + } + + static JSHandle ToPropKey(JSThread *thread, const JSHandle &key); + + // 9.4.5 Integer-Indexed Exotic Objects + // 9.4.5.1 [[GetOwnProperty]] ( P ) + static bool GetOwnProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, PropertyDescriptor &desc); + // 9.4.5.2 [[HasProperty]] ( P ) + static bool HasProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key); + // 9.4.5.3 [[DefineOwnProperty]] ( P, Desc ) + static bool DefineOwnProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const PropertyDescriptor &desc); + // 9.4.5.4 [[Get]] ( P, Receiver ) + static inline OperationResult GetProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key) + { + return GetProperty(thread, typedarray, key, typedarray); + } + static inline OperationResult GetProperty(JSThread *thread, const JSHandle &typedarray, + uint32_t index) + { + return FastElementGet(thread, typedarray, index); + } + static OperationResult GetProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const JSHandle &receiver); + // 9.4.5.5 [[Set]] ( P, V, Receiver ) + static inline bool SetProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const JSHandle &value, + bool mayThrow = false) + { + return SetProperty(thread, typedarray, key, value, typedarray, mayThrow); + } + static bool SetProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const JSHandle &value, + const JSHandle &receiver, bool mayThrow = false); + // 9.4.5.6 [[OwnPropertyKeys]] ( ) + static JSHandle OwnPropertyKeys(JSThread *thread, const JSHandle &typedarray); + // 9.4.5.7 IntegerIndexedObjectCreate (prototype, internalSlotsList) + // 9.4.5.8 IntegerIndexedElementGet ( O, index ) + static OperationResult IntegerIndexedElementGet(JSThread *thread, const JSHandle &typedarray, + JSTaggedValue index); + static OperationResult FastElementGet(JSThread *thread, const JSHandle &typedarray, uint32_t index); + // 9.4.5.9 IntegerIndexedElementSet ( O, index, value ) + static bool IntegerIndexedElementSet(JSThread *thread, const JSHandle &typedarray, + JSTaggedValue index, const JSHandle &value); + + static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; + ACCESSORS(ViewedArrayBuffer, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) + ACCESSORS(TypedArrayName, TYPED_ARRAY_NAME_OFFSET, BYTE_LENGTH_OFFSET) + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET) + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, ARRAY_LENGTH_OFFSET) + ACCESSORS(ArrayLength, ARRAY_LENGTH_OFFSET, SIZE) + + static const uint32_t MAX_TYPED_ARRAY_INDEX = MAX_ELEMENT_INDEX; + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VIEWED_ARRAY_BUFFER_OFFSET, SIZE) +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_TYPED_ARRAY_H \ No newline at end of file diff --git a/ecmascript/js_uint16_array.h b/ecmascript/js_uint16_array.h new file mode 100644 index 0000000000000000000000000000000000000000..f4fd907f8cdb61ef6bb2f3afb390aeb2cddb5329 --- /dev/null +++ b/ecmascript/js_uint16_array.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_UINT16_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_UINT16_ARRAY_H + +#include +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class JSUint16Array : public JSObject { +public: + static JSUint16Array *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSUint16Array()); + return static_cast(object); + } + + static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; + ACCESSORS(ViewedArrayBuffer, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) + ACCESSORS(TypedArrayName, TYPED_ARRAY_NAME_OFFSET, BYTE_LENGTH_OFFSET) + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET) + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, ARRAY_LENGTH_OFFSET) + ACCESSORS(ArrayLength, ARRAY_LENGTH_OFFSET, SIZE) + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VIEWED_ARRAY_BUFFER_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_UINT16_ARRAY_H \ No newline at end of file diff --git a/ecmascript/js_uint32_array.h b/ecmascript/js_uint32_array.h new file mode 100644 index 0000000000000000000000000000000000000000..f05e0d5116a57c0d5bcf772cfe47dc1b4803ef8b --- /dev/null +++ b/ecmascript/js_uint32_array.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_UINT32_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_UINT32_ARRAY_H + +#include +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class JSUint32Array : public JSObject { +public: + static JSUint32Array *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSUint32Array()); + return static_cast(object); + } + + static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; + ACCESSORS(ViewedArrayBuffer, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) + ACCESSORS(TypedArrayName, TYPED_ARRAY_NAME_OFFSET, BYTE_LENGTH_OFFSET) + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET) + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, ARRAY_LENGTH_OFFSET) + ACCESSORS(ArrayLength, ARRAY_LENGTH_OFFSET, SIZE) + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VIEWED_ARRAY_BUFFER_OFFSET, SIZE) +}; // namespace panda::ecmascript +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_UINT32_ARRAY_H \ No newline at end of file diff --git a/ecmascript/js_uint8_array.h b/ecmascript/js_uint8_array.h new file mode 100644 index 0000000000000000000000000000000000000000..a55c6a8a3454cb9cf919b6b3a35c6cafbeb573d5 --- /dev/null +++ b/ecmascript/js_uint8_array.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_UINT8_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_UINT8_ARRAY_H + +#include +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class JSUint8Array : public JSObject { +public: + static JSUint8Array *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSUint8Array()); + return static_cast(object); + } + + static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; + ACCESSORS(ViewedArrayBuffer, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) + ACCESSORS(TypedArrayName, TYPED_ARRAY_NAME_OFFSET, BYTE_LENGTH_OFFSET) + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET) + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, ARRAY_LENGTH_OFFSET) + ACCESSORS(ArrayLength, ARRAY_LENGTH_OFFSET, SIZE) + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VIEWED_ARRAY_BUFFER_OFFSET, SIZE) +}; // namespace panda::ecmascript +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_UINT8_ARRAY_H \ No newline at end of file diff --git a/ecmascript/js_uint8_clamped_array.h b/ecmascript/js_uint8_clamped_array.h new file mode 100644 index 0000000000000000000000000000000000000000..1f3aa6ae3c451768acc5da3d3a5d06c7bf29f4a5 --- /dev/null +++ b/ecmascript/js_uint8_clamped_array.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_UINT8_CLAMPED_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_UINT8_CLAMPED_ARRAY_H + +#include +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class JSUint8ClampedArray : public JSObject { +public: + static JSUint8ClampedArray *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSUint8ClampedArray()); + return static_cast(object); + } + + static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; + ACCESSORS(ViewedArrayBuffer, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) + ACCESSORS(TypedArrayName, TYPED_ARRAY_NAME_OFFSET, BYTE_LENGTH_OFFSET) + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET) + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, ARRAY_LENGTH_OFFSET) + ACCESSORS(ArrayLength, ARRAY_LENGTH_OFFSET, SIZE) + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VIEWED_ARRAY_BUFFER_OFFSET, SIZE) +}; // namespace panda::ecmascript +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_UINT8_CLAMPED_ARRAY_H \ No newline at end of file diff --git a/ecmascript/js_vm/BUILD.gn b/ecmascript/js_vm/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..aebea98b85a4f49764ae17781568e6b59b50f8ea --- /dev/null +++ b/ecmascript/js_vm/BUILD.gn @@ -0,0 +1,40 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//build/ohos.gni") + +ohos_executable("ark_js_vm") { + sources = [ "main.cpp" ] + + configs = [ + "//ark/js_runtime:ark_jsruntime_config", + "//ark/js_runtime:ark_jsruntime_public_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + "//ark/js_runtime:libark_jsruntime_static", + ] + + if (!is_standard_system) { + deps += [ "$ark_root/runtime:libarkruntime" ] + } + + install_enable = false + part_name = "ark_js_runtime" + + output_name = "ark_js_vm" + subsystem_name = "ark" +} diff --git a/ecmascript/js_vm/main.cpp b/ecmascript/js_vm/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..abaa6ee2ab97e96ef04e8281588a0d5ded282f60 --- /dev/null +++ b/ecmascript/js_vm/main.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include // NOLINTNEXTLINE(modernize-deprecated-headers) +#include + +#include "ecmascript/ecma_language_context.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/napi/include/jsnapi.h" +#include "include/runtime.h" +#include "libpandabase/os/native_stack.h" +#include "libpandabase/utils/pandargs.h" +#include "libpandabase/utils/span.h" + +namespace panda::ecmascript { +void BlockSignals() +{ +#if defined(PANDA_TARGET_UNIX) + sigset_t set; + if (sigemptyset(&set) == -1) { + LOG(ERROR, RUNTIME) << "sigemptyset failed"; + return; + } + int rc = 0; + + if (rc < 0) { + LOG(ERROR, RUNTIME) << "sigaddset failed"; + return; + } + + if (panda::os::native_stack::g_PandaThreadSigmask(SIG_BLOCK, &set, nullptr) != 0) { + LOG(ERROR, RUNTIME) << "PandaThreadSigmask failed"; + } +#endif // PANDA_TARGET_UNIX +} + +int Main(const int argc, const char **argv) +{ + auto startTime = + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count(); + + BlockSignals(); + Span sp(argv, argc); + RuntimeOptions runtimeOptions(sp[0]); + + panda::PandArg help("help", false, "Print this message and exit"); + panda::PandArg options("options", false, "Print compiler and runtime options"); + // tail arguments + panda::PandArg file("file", "", "path to pandafile"); + panda::PandArg entrypoint("entrypoint", "_GLOBAL::func_main_0", + "full name of entrypoint function or method"); + panda::PandArgParser paParser; + + runtimeOptions.AddOptions(&paParser); + + paParser.Add(&help); + paParser.Add(&options); + paParser.PushBackTail(&file); + paParser.PushBackTail(&entrypoint); + paParser.EnableTail(); + paParser.EnableRemainder(); + + if (!paParser.Parse(argc, argv) || file.GetValue().empty() || entrypoint.GetValue().empty() || help.GetValue()) { + std::cerr << paParser.GetErrorString() << std::endl; + std::cerr << "Usage: " + << "panda" + << " [OPTIONS] [file] [entrypoint] -- [arguments]" << std::endl; + std::cerr << std::endl; + std::cerr << "optional arguments:" << std::endl; + std::cerr << paParser.GetHelpString() << std::endl; + return 1; + } + + arg_list_t arguments = paParser.GetRemainder(); + + if (runtimeOptions.IsStartupTime()) { + std::cout << "\n" + << "Startup start time: " << startTime << std::endl; + } + + auto runtimeOptionsErr = runtimeOptions.Validate(); + if (runtimeOptionsErr) { + std::cerr << "Error: " << runtimeOptionsErr.value().GetMessage() << std::endl; + return 1; + } + + runtimeOptions.SetShouldLoadBootPandaFiles(false); + runtimeOptions.SetShouldInitializeIntrinsics(false); + runtimeOptions.SetBootClassSpaces({"ecmascript"}); + runtimeOptions.SetRuntimeType("ecmascript"); + static EcmaLanguageContext lcEcma; + bool ret = Runtime::Create(runtimeOptions, {&lcEcma}); + if (!ret) { + std::cerr << "Error: cannot Create Runtime" << std::endl; + return -1; + } + auto runtime = Runtime::GetCurrent(); + + if (options.GetValue()) { + std::cout << paParser.GetRegularArgs() << std::endl; + } + + std::string fileName = file.GetValue(); + std::string entry = entrypoint.GetValue(); + + EcmaVM *vm = EcmaVM::Cast(runtime->GetPandaVM()); + auto fileNameRef = StringRef::NewFromUtf8(vm, fileName.c_str(), fileName.size()); + auto entryRef = StringRef::NewFromUtf8(vm, entry.c_str(), entry.size()); + auto res = JSNApi::Execute(vm, fileNameRef, entryRef); + if (!res) { + std::cerr << "Cannot execute panda file '" << fileName << "' with entry '" << entry << "'" << std::endl; + ret = false; + } + + if (!Runtime::Destroy()) { + std::cerr << "Error: cannot destroy Runtime" << std::endl; + return -1; + } + paParser.DisableTail(); + return ret ? 0 : -1; +} +} // namespace panda::ecmascript + +int main(int argc, const char **argv) +{ + return panda::ecmascript::Main(argc, argv); +} diff --git a/ecmascript/js_weak_container.cpp b/ecmascript/js_weak_container.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7a4a132d934a107bf9c373845239694626b2b25b --- /dev/null +++ b/ecmascript/js_weak_container.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_weak_container.h" + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/linked_hash_table-inl.h" +#include "ecmascript/object_factory.h" +#include "libpandabase/utils/bit_utils.h" + +namespace panda::ecmascript { +void JSWeakMap::Set(JSThread *thread, const JSHandle &map, const JSHandle &key, + const JSHandle &value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + if (!LinkedHashMap::IsKey(JSTaggedValue(key.GetTaggedValue().CreateAndGetWeakRef()))) { + THROW_TYPE_ERROR(thread, "the value must be Key of JSMap"); + } + JSHandle mapHandle(thread, LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject())); + + auto result = LinkedHashMap::SetWeakRef(thread, mapHandle, key, value); + map->SetLinkedMap(thread, result); +} + +bool JSWeakMap::Delete(JSThread *thread, const JSHandle &map, const JSHandle &key) +{ + JSHandle mapHandle(thread, LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject())); + int entry = mapHandle->FindElement(key.GetTaggedValue()); + if (entry == -1) { + return false; + } + mapHandle->RemoveEntry(thread, entry); + + auto result = LinkedHashMap::Shrink(thread, mapHandle); + map->SetLinkedMap(thread, result); + return true; +} + +bool JSWeakMap::Has(JSTaggedValue key) const +{ + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->Has(key); +} + +JSTaggedValue JSWeakMap::Get(JSTaggedValue key) const +{ + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->Get(key); +} + +int JSWeakMap::GetSize() const +{ + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->NumberOfElements(); +} + +void JSWeakSet::Add(JSThread *thread, const JSHandle &weakSet, const JSHandle &value) +{ + if (!LinkedHashSet::IsKey(value.GetTaggedValue())) { + THROW_TYPE_ERROR(thread, "the value must be Key of JSWeakSet"); + } + JSHandle weakSetHandle(thread, LinkedHashSet::Cast(weakSet->GetLinkedSet().GetTaggedObject())); + + auto result = LinkedHashSet::AddWeakRef(thread, weakSetHandle, value); + weakSet->SetLinkedSet(thread, result); +} + +bool JSWeakSet::Delete(JSThread *thread, const JSHandle &weakSet, const JSHandle &value) +{ + JSHandle weakSetHandle(thread, LinkedHashSet::Cast(weakSet->GetLinkedSet().GetTaggedObject())); + int entry = weakSetHandle->FindElement(value.GetTaggedValue()); + if (entry == -1) { + return false; + } + weakSetHandle->RemoveEntry(thread, entry); + auto result = LinkedHashSet::Shrink(thread, weakSetHandle); + weakSet->SetLinkedSet(thread, result); + return true; +} + +bool JSWeakSet::Has(JSTaggedValue value) const +{ + return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->Has(value); +} + +int JSWeakSet::GetSize() const +{ + return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->NumberOfElements(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_weak_container.h b/ecmascript/js_weak_container.h new file mode 100644 index 0000000000000000000000000000000000000000..6217fe7652cf68755cd75e8adb3863e6c24b4206 --- /dev/null +++ b/ecmascript/js_weak_container.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_JS_WEAK_CONTAINER_H +#define PANDA_RUNTIME_ECMASCRIPT_JS_WEAK_CONTAINER_H + +#include +#include "ecmascript/js_object.h" + +namespace panda::ecmascript { +class JSWeakMap : public JSObject { +public: + static JSWeakMap *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSWeakMap()); + return static_cast(object); + } + + static bool Delete(JSThread *thread, const JSHandle &map, const JSHandle &key); + + static void Set(JSThread *thread, const JSHandle &map, const JSHandle &key, + const JSHandle &value); + + bool Has(JSTaggedValue key) const; + + JSTaggedValue Get(JSTaggedValue key) const; + + int GetSize() const; + + static constexpr size_t LINKED_MAP_OFFSET = JSObject::SIZE; + ACCESSORS(LinkedMap, LINKED_MAP_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LINKED_MAP_OFFSET, SIZE) + DECL_DUMP() +}; + +class JSWeakSet : public JSObject { +public: + static JSWeakSet *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSWeakSet()); + return static_cast(object); + } + static bool Delete(JSThread *thread, const JSHandle &set, const JSHandle &value); + + static void Add(JSThread *thread, const JSHandle &set, const JSHandle &value); + + bool Has(JSTaggedValue value) const; + + int GetSize() const; + + static constexpr size_t LINKED_SET_OFFSET = JSObject::SIZE; + ACCESSORS(LinkedSet, LINKED_SET_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LINKED_SET_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_JS_WEAK_CONTAINER_H \ No newline at end of file diff --git a/ecmascript/layout_info-inl.h b/ecmascript/layout_info-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..06b0873dc60032a4bd9c1401472a1fd947948d88 --- /dev/null +++ b/ecmascript/layout_info-inl.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_LAYOUT_INFO_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_LAYOUT_INFO_INL_H + +#include "ecmascript/layout_info.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/ic/properties_cache-inl.h" + +namespace panda::ecmascript { +inline int LayoutInfo::GetPropertiesCapacity() const +{ + return static_cast((GetLength() - ELEMENTS_START_INDEX) >> 1U); +} + +inline int LayoutInfo::NumberOfElements() const +{ + return TaggedArray::Get(NUMBER_OF_PROPERTIES_INDEX).GetInt(); +} + +inline void LayoutInfo::SetNumberOfElements(const JSThread *thread, int properties) +{ + return TaggedArray::Set(thread, NUMBER_OF_PROPERTIES_INDEX, JSTaggedValue(properties)); +} + +inline array_size_t LayoutInfo::GetKeyIndex(int index) const +{ + return ELEMENTS_START_INDEX + (static_cast(index) << 1U); +} + +inline array_size_t LayoutInfo::GetAttrIndex(int index) const +{ + return ELEMENTS_START_INDEX + (static_cast(index) << 1U) + 1; +} + +inline void LayoutInfo::SetPropertyInit(const JSThread *thread, int index, const JSTaggedValue &key, + const PropertyAttributes &attr) +{ + array_size_t fixed_idx = GetKeyIndex(index); + TaggedArray::Set(thread, fixed_idx, key); + TaggedArray::Set(thread, fixed_idx + 1, attr.GetNormalTagged()); +} + +inline void LayoutInfo::SetNormalAttr(const JSThread *thread, int index, const PropertyAttributes &attr) +{ + array_size_t fixed_idx = GetAttrIndex(index); + PropertyAttributes oldAttr(TaggedArray::Get(fixed_idx)); + oldAttr.SetNormalAttr(attr.GetNormalAttr()); + TaggedArray::Set(thread, fixed_idx, oldAttr.GetTaggedValue()); +} + +inline JSTaggedValue LayoutInfo::GetKey(int index) const +{ + array_size_t fixed_idx = GetKeyIndex(index); + return TaggedArray::Get(fixed_idx); +} + +inline PropertyAttributes LayoutInfo::GetAttr(int index) const +{ + array_size_t fixed_idx = GetAttrIndex(index); + return PropertyAttributes(TaggedArray::Get(fixed_idx)); +} + +inline JSTaggedValue LayoutInfo::GetSortedKey(int index) const +{ + array_size_t fixed_idx = GetSortedIndex(index); + return GetKey(fixed_idx); +} + +inline array_size_t LayoutInfo::GetSortedIndex(int index) const +{ + return GetAttr(index).GetSortedIndex(); +} + +inline void LayoutInfo::SetSortedIndex(const JSThread *thread, int index, int sortedIndex) +{ + array_size_t fixed_idx = GetAttrIndex(index); + PropertyAttributes attr(TaggedArray::Get(fixed_idx)); + attr.SetSortedIndex(sortedIndex); + TaggedArray::Set(thread, fixed_idx, attr.GetTaggedValue()); +} + +inline int LayoutInfo::FindElementWithCache(JSThread *thread, JSHClass *cls, JSTaggedValue key, + int propertiesNumber) +{ + ASSERT(NumberOfElements() >= propertiesNumber); + const int MAX_ELEMENTS_LINER_SEARCH = 9; // 9: Builtins Object properties number is nine; + if (propertiesNumber <= MAX_ELEMENTS_LINER_SEARCH) { + Span sp(GetProperties(), propertiesNumber); + for (int i = 0; i < propertiesNumber; i++) { + if (sp[i].key_ == key) { + return i; + } + } + return -1; + } + + PropertiesCache *cache = thread->GetEcmaVM()->GetPropertiesCache(); + int index = cache->Get(cls, key); + if (index == PropertiesCache::NOT_FOUND) { + index = BinarySearch(key, propertiesNumber); + cache->Set(cls, key, index); + } + return index; +} + +inline int LayoutInfo::BinarySearch(JSTaggedValue key, int propertiesNumber) +{ + ASSERT(NumberOfElements() >= propertiesNumber); + int low = 0; + int elements = NumberOfElements(); + int high = elements - 1; + uint32_t keyHash = key.GetKeyHashCode(); + + ASSERT(low <= high); + + while (low <= high) { + int mid = low + (high - low) / 2; // 2: half + JSTaggedValue midKey = GetSortedKey(mid); + uint32_t midHash = midKey.GetKeyHashCode(); + if (midHash > keyHash) { + high = mid - 1; + } else if (midHash < keyHash) { + low = mid + 1; + } else { + int sortIndex = GetSortedIndex(mid); + JSTaggedValue currentKey = GetKey(sortIndex); + if (currentKey == key) { + return sortIndex < propertiesNumber ? sortIndex : -1; + } + int midLeft = mid; + int midRight = mid; + while (midLeft - 1 >= 0) { + sortIndex = GetSortedIndex(--midLeft); + currentKey = GetKey(sortIndex); + if (currentKey.GetKeyHashCode() == keyHash) { + if (currentKey == key) { + return sortIndex < propertiesNumber ? sortIndex : -1; + } + } else { + break; + } + } + while (midRight + 1 < elements) { + sortIndex = GetSortedIndex(++midRight); + currentKey = GetKey(sortIndex); + if (currentKey.GetKeyHashCode() == keyHash) { + if (currentKey == key) { + return sortIndex < propertiesNumber ? sortIndex : -1; + } + } else { + break; + } + } + return -1; + } + } + return -1; +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_LAYOUT_INFO_INL_H diff --git a/ecmascript/layout_info.cpp b/ecmascript/layout_info.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3d5bf8ea1a661385dd556fae3b65f6f836dd6f0 --- /dev/null +++ b/ecmascript/layout_info.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/layout_info-inl.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/js_symbol.h" +#include "ecmascript/mem/assert_scope-inl.h" + +namespace panda::ecmascript { +void LayoutInfo::AddKey(const JSThread *thread, [[maybe_unused]] int index, const JSTaggedValue &key, + const PropertyAttributes &attr) +{ + DISALLOW_GARBAGE_COLLECTION; + int number = NumberOfElements(); + ASSERT(attr.GetOffset() == static_cast(number)); + ASSERT(number + 1 <= GetPropertiesCapacity()); + ASSERT(number == index); + SetNumberOfElements(thread, number + 1); + SetPropertyInit(thread, number, key, attr); + + uint32_t keyHash = key.GetKeyHashCode(); + int insertIndex = number; + for (; insertIndex > 0; --insertIndex) { + JSTaggedValue prevKey = GetSortedKey(insertIndex - 1); + if (prevKey.GetKeyHashCode() <= keyHash) { + break; + } + SetSortedIndex(thread, insertIndex, GetSortedIndex(insertIndex - 1)); + } + SetSortedIndex(thread, insertIndex, number); +} + +void LayoutInfo::GetAllKeys(const JSThread *thread, int end, int offset, TaggedArray *keyArray) +{ + ASSERT(end <= NumberOfElements()); + ASSERT_PRINT(offset + end <= static_cast(keyArray->GetLength()), + "keyArray capacity is not enough for dictionary"); + + DISALLOW_GARBAGE_COLLECTION; + int enumKeys = 0; + for (int i = 0; i < end; i++) { + JSTaggedValue key = GetKey(i); + if (key.IsString()) { + keyArray->Set(thread, enumKeys + offset, key); + enumKeys++; + } + } + + if (enumKeys < end) { + for (int i = 0; i < end; i++) { + JSTaggedValue key = GetKey(i); + if (key.IsSymbol()) { + keyArray->Set(thread, enumKeys + offset, key); + enumKeys++; + } + } + } +} + +void LayoutInfo::GetAllKeys(const JSThread *thread, int end, std::vector &keyVector) +{ + ASSERT(end <= NumberOfElements()); + int enumKeys = 0; + for (int i = 0; i < end; i++) { + JSTaggedValue key = GetKey(i); + if (key.IsString()) { + keyVector.emplace_back(key); + enumKeys++; + } + } +} + +void LayoutInfo::GetAllEnumKeys(const JSThread *thread, int end, int offset, TaggedArray *keyArray, + array_size_t *keys) +{ + ASSERT(end <= NumberOfElements()); + ASSERT_PRINT(offset + end <= static_cast(keyArray->GetLength()), + "keyArray capacity is not enough for dictionary"); + + DISALLOW_GARBAGE_COLLECTION; + int enumKeys = 0; + for (int i = 0; i < end; i++) { + JSTaggedValue key = GetKey(i); + if (key.IsString() && GetAttr(i).IsEnumerable()) { + keyArray->Set(thread, enumKeys + offset, key); + enumKeys++; + } + } + *keys += enumKeys; +} + +void LayoutInfo::GetAllNames(const JSThread *thread, int end, const JSHandle &keyArray, + array_size_t *length) +{ + DISALLOW_GARBAGE_COLLECTION; + int arrayIndex = 0; + for (int i = 0; i < end; i++) { + JSTaggedValue key = GetKey(i); + if (key.IsString()) { + PropertyAttributes attr = GetAttr(i); + if (attr.IsEnumerable()) { + keyArray->Set(thread, arrayIndex++, key); + } + } + } + *length += arrayIndex; +} +} // namespace panda::ecmascript diff --git a/ecmascript/layout_info.h b/ecmascript/layout_info.h new file mode 100644 index 0000000000000000000000000000000000000000..64a3e4af9bf16ff5bb8e53d09595344cffde41b1 --- /dev/null +++ b/ecmascript/layout_info.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_LAYOUT_INFO_H +#define PANDA_RUNTIME_ECMASCRIPT_LAYOUT_INFO_H + +#include "ecmascript/tagged_array.h" +#include "ecmascript/property_attributes.h" +#include "ecmascript/js_object.h" + +namespace panda::ecmascript { +struct Properties { + JSTaggedValue key_; + JSTaggedValue attr_; +}; + +class LayoutInfo : private TaggedArray { +public: + static constexpr int MIN_PROPERTIES_LENGTH = JSObject::MIN_PROPERTIES_LENGTH; + static constexpr int MAX_PROPERTIES_LENGTH = PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES; + static constexpr int NUMBER_OF_PROPERTIES_INDEX = 0; + static constexpr int ELEMENTS_START_INDEX = 1; + + inline static LayoutInfo *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsTaggedArray()); + return reinterpret_cast(obj); + } + + int GetPropertiesCapacity() const; + int NumberOfElements() const; + void SetNumberOfElements(const JSThread *thread, int properties); + array_size_t GetKeyIndex(int index) const; + array_size_t GetAttrIndex(int index) const; + void SetPropertyInit(const JSThread *thread, int index, const JSTaggedValue &key, const PropertyAttributes &attr); + void SetKey(const JSThread *thread, int index, const JSTaggedValue &key); + void SetNormalAttr(const JSThread *thread, int index, const PropertyAttributes &attr); + JSTaggedValue GetKey(int index) const; + PropertyAttributes GetAttr(int index) const; + JSTaggedValue GetSortedKey(int index) const; + array_size_t GetSortedIndex(int index) const; + void SetSortedIndex(const JSThread *thread, int index, int sortedIndex); + void AddKey(const JSThread *thread, int index, const JSTaggedValue &key, const PropertyAttributes &attr); + + inline array_size_t GetLength() const + { + return TaggedArray::GetLength(); + } + + inline Properties *GetProperties() const + { + return reinterpret_cast(reinterpret_cast(this) + TaggedArray::GetDataOffset() + + ELEMENTS_START_INDEX * JSTaggedValue::TaggedTypeSize()); + } + + static inline array_size_t ComputeArrayLength(array_size_t properties_number) + { + return (properties_number << 1U) + ELEMENTS_START_INDEX; + } + + static inline array_size_t ComputeGrowCapacity(uint32_t old_capacity) + { + array_size_t new_capacity = old_capacity + MIN_PROPERTIES_LENGTH; + return new_capacity > MAX_PROPERTIES_LENGTH ? MAX_PROPERTIES_LENGTH : new_capacity; + } + + int FindElementWithCache(JSThread *thread, JSHClass *cls, JSTaggedValue key, int propertiesNumber); + int FindElement(JSTaggedValue key, int propertiesNumber); + int BinarySearch(JSTaggedValue key, int propertiesNumber); + void GetAllKeys(const JSThread *thread, int end, int offset, TaggedArray *keyArray); + void GetAllKeys(const JSThread *thread, int end, std::vector &keyVector); + void GetAllEnumKeys(const JSThread *thread, int end, int offset, TaggedArray *keyArray, array_size_t *keys); + void GetAllNames(const JSThread *thread, int end, const JSHandle &keyArray, array_size_t *length); + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_LAYOUT_INFO_H diff --git a/ecmascript/lexical_env.h b/ecmascript/lexical_env.h new file mode 100644 index 0000000000000000000000000000000000000000..a504fa0cd919e79a10edc2cdd545fd443342b6f2 --- /dev/null +++ b/ecmascript/lexical_env.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_LEXICALENV_H +#define PANDA_RUNTIME_ECMASCRIPT_LEXICALENV_H + +#include "ecmascript/js_object.h" + +namespace panda::ecmascript { +class LexicalEnv : public TaggedArray { +public: + static constexpr array_size_t PARENT_ENV_INDEX = 0; + static constexpr array_size_t RESERVED_ENV_LENGTH = 1; + + static LexicalEnv *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsTaggedArray()); + return static_cast(object); + } + + static size_t ComputeSize(uint32_t numSlots) + { + return TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), numSlots + RESERVED_ENV_LENGTH); + } + + void SetParentEnv(JSThread *thread, JSTaggedValue value) + { + Set(thread, PARENT_ENV_INDEX, value); + } + + JSTaggedValue GetParentEnv() const + { + return Get(PARENT_ENV_INDEX); + } + + JSTaggedValue GetProperties(uint32_t index) const + { + return Get(index + RESERVED_ENV_LENGTH); + } + + void SetProperties(JSThread *thread, uint32_t index, JSTaggedValue value) + { + Set(thread, index + RESERVED_ENV_LENGTH, value); + } + + DECL_DUMP() +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_LEXICALENV_H diff --git a/ecmascript/linked_hash_table-inl.h b/ecmascript/linked_hash_table-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..ee007aa70d90c2ef0b5a0a9c69587287ea66e444 --- /dev/null +++ b/ecmascript/linked_hash_table-inl.h @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_LINKED_HASH_TABLE_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_LINKED_HASH_TABLE_INL_H + +#include "linked_hash_table.h" +#include "tagged_array-inl.h" +#include "utils/bit_utils.h" + +namespace panda::ecmascript { +template +JSTaggedValue LinkedHashTable::GetElement(int index) const +{ + if (UNLIKELY((index < 0 || index > static_cast(GetLength())))) { + return JSTaggedValue::Undefined(); + } + return Get(index); +} + +template +void LinkedHashTable::SetElement(const JSThread *thread, int index, JSTaggedValue element) +{ + if (UNLIKELY((index < 0 || index > static_cast(GetLength())))) { + return; + } + Set(thread, index, element); +} + +template +int LinkedHashTable::NumberOfElements() const +{ + return Get(NUMBER_OF_ELEMENTS_INDEX).GetInt(); +} + +template +int LinkedHashTable::NumberOfDeletedElements() const +{ + return Get(NUMBER_OF_DELETED_ELEMENTS_INDEX).GetInt(); +} + +template +int LinkedHashTable::Capacity() const +{ + return JSTaggedValue(Get(CAPACITY_INDEX)).GetInt(); +} + +template +void LinkedHashTable::SetNumberOfElements(const JSThread *thread, int nof) +{ + Set(thread, NUMBER_OF_ELEMENTS_INDEX, JSTaggedValue(nof)); +} + +template +void LinkedHashTable::SetNumberOfDeletedElements(const JSThread *thread, int nod) +{ + Set(thread, NUMBER_OF_DELETED_ELEMENTS_INDEX, JSTaggedValue(nod)); +} + +template +void LinkedHashTable::SetCapacity(const JSThread *thread, int capacity) +{ + Set(thread, CAPACITY_INDEX, JSTaggedValue(capacity)); +} + +template +void LinkedHashTable::SetNextTable(const JSThread *thread, JSTaggedValue nextTable) +{ + Set(thread, NEXT_TABLE_INDEX, nextTable); +} + +template +JSTaggedValue LinkedHashTable::GetNextTable() const +{ + return JSTaggedValue(Get(NEXT_TABLE_INDEX)); +} + +template +int LinkedHashTable::GetDeletedNum(int entry) const +{ + ASSERT_PRINT(!GetNextTable().IsUndefined(), "function only execute after rehash"); + return GetNextEntry(entry).GetInt(); +} + +template +void LinkedHashTable::SetDeletedNum(const JSThread *thread, int entry, JSTaggedValue num) +{ + ASSERT_PRINT(!GetNextTable().IsUndefined(), "function only execute after rehash"); + SetNextEntry(thread, entry, num); +} + +template +int LinkedHashTable::GetDeletedElementsAt(int entry) const +{ + ASSERT_PRINT(!GetNextTable().IsUndefined(), "function only execute after rehash"); + int currentEntry = entry - 1; + while (currentEntry >= 0) { + if (GetKey(currentEntry).IsHole()) { + return GetDeletedNum(currentEntry); + } + currentEntry--; + } + return 0; +} + +template +uint32_t LinkedHashTable::HashToBucket(uint32_t hash) const +{ + return hash & static_cast(Capacity() - 1); +} + +template +uint32_t LinkedHashTable::BucketToIndex(uint32_t bucket) +{ + return bucket + ELEMENTS_START_INDEX; +} + +template +uint32_t LinkedHashTable::EntryToIndex(uint32_t entry) const +{ + return ELEMENTS_START_INDEX + Capacity() + entry * (HashObject::ENTRY_SIZE + 1); +} + +template +void LinkedHashTable::SetKey(const JSThread *thread, int entry, JSTaggedValue key) +{ + int index = EntryToIndex(entry); + SetElement(thread, index, key); +} + +template +JSTaggedValue LinkedHashTable::GetKey(int entry) const +{ + int index = EntryToIndex(entry); + return GetElement(index); +} + +template +JSTaggedValue LinkedHashTable::GetValue(int entry) const +{ + int index = EntryToIndex(entry) + HashObject::ENTRY_VALUE_INDEX; + return GetElement(index); +} + +template +void LinkedHashTable::SetValue(const JSThread *thread, int entry, JSTaggedValue value) +{ + int index = EntryToIndex(entry) + HashObject::ENTRY_VALUE_INDEX; + SetElement(thread, index, value); +} + +template +JSTaggedValue LinkedHashTable::GetNextEntry(int entry) const +{ + int index = EntryToIndex(entry) + HashObject::ENTRY_SIZE; + return GetElement(index); +} + +template +void LinkedHashTable::SetNextEntry(const JSThread *thread, int entry, JSTaggedValue nextEntry) +{ + int index = EntryToIndex(entry) + HashObject::ENTRY_SIZE; + SetElement(thread, index, nextEntry); +} + +template +void LinkedHashTable::InsertNewEntry(const JSThread *thread, int bucket, int entry) +{ + int bucketIndex = BucketToIndex(bucket); + JSTaggedValue previousEntry = GetElement(bucketIndex); + SetNextEntry(thread, entry, previousEntry); + SetElement(thread, bucketIndex, JSTaggedValue(entry)); +} + +template +int LinkedHashTable::FindElement(JSTaggedValue key) const +{ + if (!IsKey(key)) { + return -1; + } + int hash = HashObject::Hash(key); + int bucket = HashToBucket(hash); + for (JSTaggedValue entry = GetElement(BucketToIndex(bucket)); !entry.IsHole(); + entry = GetNextEntry(entry.GetInt())) { + JSTaggedValue element = GetKey(entry.GetInt()); + if (element.IsHole()) { + continue; + } + if (element.IsWeak()) { + element.RemoveWeakTag(); + } + if (HashObject::IsMatch(key, element)) { + return entry.GetInt(); + } + } + return -1; +} // namespace panda::ecmascript + +template +bool LinkedHashTable::HasSufficientCapacity(int numOfAddElements) const +{ + int numberOfElements = NumberOfElements(); + int numOfDelElements = NumberOfDeletedElements(); + int capacity = Capacity(); + int nof = numberOfElements + numOfAddElements; + // Return true if: + // 50% is still free after adding numOfAddElements elements and + // at most 50% of the free elements are deleted elements. + if ((nof < capacity) && ((numOfDelElements <= (capacity - nof) / 2))) { // 2: half + int neededFree = nof / 2; // 2: half + if (nof + neededFree <= capacity) { + return true; + } + } + return false; +} + +template +int LinkedHashTable::ComputeCapacity(uint32_t atLeastSpaceFor) +{ + // Add 50% slack to make slot collisions sufficiently unlikely. + // See matching computation in HashTable::HasSufficientCapacity(). + uint32_t rawCap = atLeastSpaceFor + (atLeastSpaceFor >> 1UL); + int capacity = static_cast(helpers::math::GetPowerOfTwoValue32(rawCap)); + return (capacity > MIN_CAPACITY) ? capacity : MIN_CAPACITY; +} + +template +void LinkedHashTable::RemoveEntry(const JSThread *thread, int entry) +{ + ASSERT_PRINT(entry >= 0 && entry < Capacity(), "entry must be a non-negative integer less than capacity"); + int index = EntryToIndex(entry); + for (int i = 0; i < HashObject::ENTRY_SIZE; i++) { + SetElement(thread, index + i, JSTaggedValue::Hole()); + } + SetNumberOfElements(thread, NumberOfElements() - 1); + SetNumberOfDeletedElements(thread, NumberOfDeletedElements() + 1); +} + +template +int LinkedHashTable::ComputeCapacityWithShrink(int currentCapacity, int atLeastSpaceFor) +{ + // Shrink to fit the number of elements if only a quarter of the + // capacity is filled with elements. + if (atLeastSpaceFor > (currentCapacity / 4)) { // 4: quarter + return currentCapacity; + } + // Recalculate the smaller capacity actually needed. + int newCapacity = ComputeCapacity(atLeastSpaceFor); + ASSERT_PRINT(newCapacity > atLeastSpaceFor, "new capacity must greater than atLeastSpaceFor"); + // Don't go lower than room for MIN_SHRINK_CAPACITY elements. + if (newCapacity < Derived::MIN_SHRINK_CAPACITY) { + return currentCapacity; + } + return newCapacity; +} + +bool LinkedHashMapObject::IsMatch(JSTaggedValue key, JSTaggedValue other) +{ + return JSTaggedValue::SameValueZero(key, other); +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_LINKED_HASH_TABLE_INL_H \ No newline at end of file diff --git a/ecmascript/linked_hash_table.cpp b/ecmascript/linked_hash_table.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f79ba560eb05739f64d744fbb30ede0d3c41a8d --- /dev/null +++ b/ecmascript/linked_hash_table.cpp @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_object-inl.h" + +#include "libpandabase/utils/bit_utils.h" +#include "linked_hash_table-inl.h" +#include "object_factory.h" + +namespace panda::ecmascript { +template +Derived *LinkedHashTable::Create(const JSThread *thread, int numberOfElements) +{ + ASSERT_PRINT(numberOfElements > 0, "size must be a non-negative integer"); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto capacity = static_cast(numberOfElements); + ASSERT_PRINT(helpers::math::IsPowerOfTwo(capacity), "capacity must be pow of '2'"); + int length = ELEMENTS_START_INDEX + numberOfElements + numberOfElements * (HashObject::ENTRY_SIZE + 1); + + auto table = static_cast(*factory->NewTaggedArray(length)); + table->SetNumberOfElements(thread, 0); + table->SetNumberOfDeletedElements(thread, 0); + table->SetCapacity(thread, capacity); + return table; +} + +template +Derived *LinkedHashTable::Insert(const JSThread *thread, const JSHandle &table, + const JSHandle &key, + const JSHandle &value) +{ + ASSERT(IsKey(key.GetTaggedValue())); + int hash = HashObject::Hash(key.GetTaggedValue()); + int entry = table->FindElement(key.GetTaggedValue()); + if (entry != -1) { + table->SetValue(thread, entry, value.GetTaggedValue()); + return Derived::Cast(*table); + } + + Derived *newTable = GrowCapacity(thread, table); + + int bucket = newTable->HashToBucket(hash); + entry = newTable->NumberOfElements() + newTable->NumberOfDeletedElements(); + newTable->InsertNewEntry(thread, bucket, entry); + newTable->SetKey(thread, entry, key.GetTaggedValue()); + newTable->SetValue(thread, entry, value.GetTaggedValue()); + newTable->SetNumberOfElements(thread, newTable->NumberOfElements() + 1); + + return newTable; +} + +template +Derived *LinkedHashTable::InsertWeakRef(const JSThread *thread, const JSHandle &table, + const JSHandle &key, + const JSHandle &value) +{ + ASSERT(IsKey(key.GetTaggedValue())); + int hash = HashObject::Hash(key.GetTaggedValue()); + int entry = table->FindElement(key.GetTaggedValue()); + if (entry != -1) { + table->SetValue(thread, entry, value.GetTaggedValue()); + return Derived::Cast(*table); + } + + Derived *newTable = GrowCapacity(thread, table); + + int bucket = newTable->HashToBucket(hash); + entry = newTable->NumberOfElements() + newTable->NumberOfDeletedElements(); + newTable->InsertNewEntry(thread, bucket, entry); + JSTaggedValue weakKey(key->CreateAndGetWeakRef()); + newTable->SetKey(thread, entry, weakKey); + // The ENTRY_VALUE_INDEX of LinkedHashSet is 0. SetValue will cause the overwitten key. + if (std::is_same_v) { + newTable->SetValue(thread, entry, value.GetTaggedValue()); + } + newTable->SetNumberOfElements(thread, newTable->NumberOfElements() + 1); + + return newTable; +} + +template +void LinkedHashTable::Rehash(const JSThread *thread, Derived *newTable) +{ + ASSERT_PRINT(newTable != nullptr && newTable->Capacity() > NumberOfElements(), "can not rehash to new table"); + // Rehash elements to new table + int numberOfAllElements = NumberOfElements() + NumberOfDeletedElements(); + int desEntry = 0; + int currentDeletedElements = 0; + SetNextTable(thread, JSTaggedValue(newTable)); + for (int i = 0; i < numberOfAllElements; i++) { + int fromIndex = EntryToIndex(i); + JSTaggedValue key = GetElement(fromIndex); + if (key.IsHole()) { + // store num_of_deleted_element before entry i; it will be used when iterator update. + currentDeletedElements++; + SetDeletedNum(thread, i, JSTaggedValue(currentDeletedElements)); + continue; + } + + if (key.IsWeak()) { + // If the key is a weak reference, we use the weak referent to calculate the new index in the new table. + key.RemoveWeakTag(); + } + + int bucket = newTable->HashToBucket(HashObject::Hash(key)); + newTable->InsertNewEntry(thread, bucket, desEntry); + int desIndex = newTable->EntryToIndex(desEntry); + for (int j = 0; j < HashObject::ENTRY_SIZE; j++) { + newTable->SetElement(thread, desIndex + j, GetElement(fromIndex + j)); + } + desEntry++; + } + newTable->SetNumberOfElements(thread, NumberOfElements()); + newTable->SetNumberOfDeletedElements(thread, 0); +} + +template +Derived *LinkedHashTable::GrowCapacity(const JSThread *thread, const JSHandle &table, + int numberOfAddedElements) +{ + if (table->HasSufficientCapacity(numberOfAddedElements)) { + return Derived::Cast(*table); + } + int newCapacity = ComputeCapacity(table->NumberOfElements() + numberOfAddedElements); + Derived *newTable = Create(thread, newCapacity); + table->Rehash(thread, newTable); + return newTable; +} + +template +Derived *LinkedHashTable::Remove(const JSThread *thread, const JSHandle &table, + const JSHandle &key) +{ + int entry = table->FindElement(key.GetTaggedValue()); + if (entry == -1) { + return Derived::Cast(*table); + } + + table->RemoveEntry(thread, entry); + return Shrink(thread, table); +} + +template +Derived *LinkedHashTable::Shrink(const JSThread *thread, const JSHandle &table, + int additionalCapacity) +{ + int newCapacity = ComputeCapacityWithShrink(table->Capacity(), table->NumberOfElements() + additionalCapacity); + if (newCapacity == table->Capacity()) { + return Derived::Cast(*table); + } + + Derived *newTable = Create(thread, newCapacity); + + table->Rehash(thread, newTable); + return newTable; +} + +// LinkedHashMap +JSTaggedValue LinkedHashMap::Create(const JSThread *thread, int numberOfElements) +{ + return JSTaggedValue(LinkedHashTable::Create(thread, numberOfElements)); +} + +JSTaggedValue LinkedHashMap::Delete(const JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + return JSTaggedValue(LinkedHashTable::Remove(thread, obj, key)); +} + +JSTaggedValue LinkedHashMap::Set(const JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value) +{ + return JSTaggedValue(LinkedHashTable::Insert(thread, obj, key, value)); +} + +JSTaggedValue LinkedHashMap::SetWeakRef(const JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value) +{ + return JSTaggedValue(LinkedHashTable::InsertWeakRef(thread, obj, key, value)); +} + +JSTaggedValue LinkedHashMap::Get(JSTaggedValue key) const +{ + int entry = FindElement(key); + if (entry == -1) { + return JSTaggedValue::Undefined(); + } + return GetValue(entry); +} + +bool LinkedHashMap::Has(JSTaggedValue key) const +{ + int entry = FindElement(key); + return entry != -1; +} + +void LinkedHashMap::Clear(const JSThread *thread) +{ + int numberOfElements = NumberOfElements() + NumberOfDeletedElements(); + for (int entry = 0; entry < numberOfElements; entry++) { + SetKey(thread, entry, JSTaggedValue::Hole()); + SetValue(thread, entry, JSTaggedValue::Hole()); + } + SetNumberOfElements(thread, 0); + SetNumberOfDeletedElements(thread, numberOfElements); +} + +JSTaggedValue LinkedHashMap::Shrink(const JSThread *thread, const JSHandle &table, + int additionalCapacity) +{ + return JSTaggedValue( + LinkedHashTable::Shrink(thread, table, additionalCapacity)); +} + +// LinkedHashSet +JSTaggedValue LinkedHashSet::Create(const JSThread *thread, int numberOfElements) +{ + return JSTaggedValue(LinkedHashTable::Create(thread, numberOfElements)); +} + +JSTaggedValue LinkedHashSet::Delete(const JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + return JSTaggedValue(LinkedHashTable::Remove(thread, obj, key)); +} + +JSTaggedValue LinkedHashSet::Add(const JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + return JSTaggedValue(LinkedHashTable::Insert(thread, obj, key, key)); +} + +JSTaggedValue LinkedHashSet::AddWeakRef(const JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + return JSTaggedValue(LinkedHashTable::InsertWeakRef(thread, obj, key, key)); +} + +bool LinkedHashSet::Has(JSTaggedValue key) const +{ + int entry = FindElement(key); + return entry != -1; +} + +void LinkedHashSet::Clear(const JSThread *thread) +{ + int numberOfElements = NumberOfElements() + NumberOfDeletedElements(); + for (int entry = 0; entry < numberOfElements; entry++) { + SetKey(thread, entry, JSTaggedValue::Hole()); + } + SetNumberOfElements(thread, 0); + SetNumberOfDeletedElements(thread, numberOfElements); +} + +JSTaggedValue LinkedHashSet::Shrink(const JSThread *thread, const JSHandle &table, + int additionalCapacity) +{ + return JSTaggedValue( + LinkedHashTable::Shrink(thread, table, additionalCapacity)); +} + +int LinkedHashMapObject::Hash(JSTaggedValue key) +{ + if (key.IsDouble() && key.GetDouble() == 0.0) { + key = JSTaggedValue(0); + } + if (key.IsSymbol()) { + auto symbolString = JSSymbol::Cast(key.GetHeapObject()); + return static_cast(symbolString->GetHashField()).GetInt(); + } + if (key.IsString()) { + auto keyString = reinterpret_cast(key.GetHeapObject()); + return keyString->GetHashcode(); + } + if (key.IsECMAObject()) { + int32_t hash = ECMAObject::Cast(key.GetHeapObject())->GetHash(); + if (hash == 0) { + uint64_t keyValue = key.GetRawData(); + hash = GetHash32(reinterpret_cast(&keyValue), sizeof(keyValue) / sizeof(uint8_t)); + ECMAObject::Cast(key.GetHeapObject())->SetHash(hash); + } + return hash; + } + + // Int, Double, Special and HeapObject(except symbol and string) + uint64_t keyValue = key.GetRawData(); + return GetHash32(reinterpret_cast(&keyValue), sizeof(keyValue) / sizeof(uint8_t)); +} +} // namespace panda::ecmascript diff --git a/ecmascript/linked_hash_table.h b/ecmascript/linked_hash_table.h new file mode 100644 index 0000000000000000000000000000000000000000..ec5bb124e4e32ea235e88033cad32be4e3a9acbb --- /dev/null +++ b/ecmascript/linked_hash_table.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_LINKED_HASH_TABLE_H +#define PANDA_RUNTIME_ECMASCRIPT_LINKED_HASH_TABLE_H + +#include "ecmascript/js_tagged_value.h" +#include "js_handle.h" +#include "js_symbol.h" +#include "js_tagged_number.h" +#include "tagged_array.h" + +namespace panda::ecmascript { +/** + * memory in LinkedHashTable is divided into 3 parts + * 1.array[0-2] is used to store common information of hashtale such as numberOfElements and capacity + * 2.array[3,3+capacity] is buckets which store the position of entry + * 3.array[3+capacity+1,3+capacity + capacity*(entry_size+1)] is the entry stored in order, the last element of an entry + * is a number which point to next entry. + * */ +template +class LinkedHashTable : public TaggedArray { +public: + static const int MIN_CAPACITY = 4; + static const int NUMBER_OF_ELEMENTS_INDEX = 0; + static const int NUMBER_OF_DELETED_ELEMENTS_INDEX = 1; + static const int CAPACITY_INDEX = 2; + static const int NEXT_TABLE_INDEX = 3; + static const int ELEMENTS_START_INDEX = 4; + // Don't shrink a HashTable below this capacity. + static const int MIN_SHRINK_CAPACITY = 16; + + static Derived *Create(const JSThread *thread, int numberOfElements); + + static Derived *Insert(const JSThread *thread, const JSHandle &table, const JSHandle &key, + const JSHandle &value); + + static Derived *InsertWeakRef(const JSThread *thread, const JSHandle &table, + const JSHandle &key, const JSHandle &value); + + static Derived *GrowCapacity(const JSThread *thread, const JSHandle &table, int numberOfAddedElements = 1); + + static Derived *Remove(const JSThread *thread, const JSHandle &table, const JSHandle &key); + + static Derived *Shrink(const JSThread *thread, const JSHandle &table, int additionalCapacity = 0); + + void Rehash(const JSThread *thread, Derived *newTable); + + inline bool HasSufficientCapacity(int numOfAddElements) const; + + inline int FindElement(JSTaggedValue key) const; + + inline void RemoveEntry(const JSThread *thread, int entry); + + inline static int ComputeCapacity(uint32_t atLeastSpaceFor); + + inline static int ComputeCapacityWithShrink(int currentCapacity, int atLeastSpaceFor); + + inline int NumberOfElements() const; + + inline int NumberOfDeletedElements() const; + + inline int Capacity() const; + + inline JSTaggedValue GetKey(int entry) const; + + inline JSTaggedValue GetValue(int entry) const; + + inline static bool IsKey(JSTaggedValue key) + { + return !key.IsHole(); + } + + inline void SetNumberOfElements(const JSThread *thread, int nof); + + inline void SetNumberOfDeletedElements(const JSThread *thread, int nod); + + inline void SetCapacity(const JSThread *thread, int capacity); + + inline JSTaggedValue GetNextTable() const; + + inline void SetNextTable(const JSThread *thread, JSTaggedValue nextTable); + + inline int GetDeletedElementsAt(int entry) const; + +protected: + inline JSTaggedValue GetElement(int index) const; + + inline void SetElement(const JSThread *thread, int index, JSTaggedValue element); + + inline void SetKey(const JSThread *thread, int entry, JSTaggedValue key); + + inline void SetValue(const JSThread *thread, int entry, JSTaggedValue value); + + inline JSTaggedValue GetNextEntry(int entry) const; + + inline void SetNextEntry(const JSThread *thread, int entry, JSTaggedValue nextEntry); + + inline uint32_t HashToBucket(uint32_t hash) const; + + inline static uint32_t BucketToIndex(uint32_t bucket); + + // min entry = 0 + inline uint32_t EntryToIndex(uint32_t entry) const; + + inline void InsertNewEntry(const JSThread *thread, int bucket, int entry); + + inline int GetDeletedNum(int entry) const; + + inline void SetDeletedNum(const JSThread *thread, int entry, JSTaggedValue num); +}; + +class LinkedHashMapObject { +public: + // key must be string now for other object has no 'equals' method + static inline bool IsMatch(JSTaggedValue key, JSTaggedValue other); + + static int Hash(JSTaggedValue key); + + static const int ENTRY_SIZE = 2; + static const int ENTRY_VALUE_INDEX = 1; +}; + +class LinkedHashMap : public LinkedHashTable { +public: + static LinkedHashMap *Cast(ObjectHeader *obj) + { + return static_cast(obj); + } + static JSTaggedValue Create(const JSThread *thread, int numberOfElements = MIN_CAPACITY); + + static JSTaggedValue Delete(const JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static JSTaggedValue Set(const JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value); + + static JSTaggedValue SetWeakRef(const JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value); + + JSTaggedValue Get(JSTaggedValue key) const; + + static JSTaggedValue Shrink(const JSThread *thread, const JSHandle &table, + int additionalCapacity = 0); + + bool Has(JSTaggedValue key) const; + + void Clear(const JSThread *thread); + DECL_DUMP() +}; + +class LinkedHashSetObject { +public: + // key must be string now for other object has no 'equals' method + static inline bool IsMatch(JSTaggedValue key, JSTaggedValue other) + { + return JSTaggedValue::SameValueZero(key, other); + } + + static inline int Hash(JSTaggedValue key) + { + if (key.IsDouble() && key.GetDouble() == 0.0) { + key = JSTaggedValue(0); + } + if (key.IsSymbol()) { + auto symbolString = JSSymbol::Cast(key.GetTaggedObject()); + return static_cast(symbolString->GetHashField()).GetInt(); + } + if (key.IsString()) { + auto keyString = reinterpret_cast(key.GetTaggedObject()); + return keyString->GetHashcode(); + } + + // Int, Double, Special and HeapObject(except symbol and string) + uint64_t keyValue = key.GetRawData(); + return GetHash32(reinterpret_cast(&keyValue), sizeof(keyValue) / sizeof(uint8_t)); + } + + static const int ENTRY_SIZE = 1; + static const int ENTRY_VALUE_INDEX = 0; +}; + +class LinkedHashSet : public LinkedHashTable { +public: + static LinkedHashSet *Cast(ObjectHeader *obj) + { + return static_cast(obj); + } + static JSTaggedValue Create(const JSThread *thread, int numberOfElements = MIN_CAPACITY); + + static JSTaggedValue Delete(const JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static JSTaggedValue Add(const JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static JSTaggedValue AddWeakRef(const JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static JSTaggedValue Shrink(const JSThread *thread, const JSHandle &table, + int additionalCapacity = 0); + + bool Has(JSTaggedValue key) const; + + void Clear(const JSThread *thread); + DECL_DUMP() +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_LINKED_HASH_TABLE_H diff --git a/ecmascript/literal_data_extractor.cpp b/ecmascript/literal_data_extractor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7a847fd6fff29680c45afe882e8dfaa749a94a56 --- /dev/null +++ b/ecmascript/literal_data_extractor.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "literal_data_extractor.h" +#include "ecmascript/base/string_helper.h" +#include "ecmascript/ecma_string.h" + +#include "ecmascript/js_thread.h" +#include "ecmascript/tagged_array-inl.h" +#include "libpandafile/literal_data_accessor-inl.h" + +namespace panda::ecmascript { +using LiteralTag = panda_file::LiteralTag; +using StringData = panda_file::StringData; +using LiteralValue = panda_file::LiteralDataAccessor::LiteralValue; + +void LiteralDataExtractor::ExtractObjectDatas(JSThread *thread, const panda_file::File *pf, size_t index, + JSMutableHandle elements, + JSMutableHandle properties, PandaFileTranslator *pft) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + LOG_ECMA(DEBUG) << "Panda File" << pf->GetFilename(); + panda_file::File::EntityId literalArraysId = pf->GetLiteralArraysId(); + panda_file::LiteralDataAccessor lda(*pf, literalArraysId); + + uint32_t num = lda.GetLiteralValsNum(index) / 2; // 2: half + elements.Update(factory->NewTaggedArray(num).GetTaggedValue()); + properties.Update(factory->NewTaggedArray(num).GetTaggedValue()); + array_size_t epos = 0; + array_size_t ppos = 0; + const uint8_t pairSize = 2; + lda.EnumerateLiteralVals( + index, [elements, properties, &epos, &ppos, factory, thread, pft](const LiteralValue &value, + const LiteralTag &tag) { + JSTaggedValue jt = JSTaggedValue::Null(); + bool flag = false; + switch (tag) { + case LiteralTag::INTEGER: { + jt = JSTaggedValue(std::get(value)); + break; + } + case LiteralTag::DOUBLE: { + jt = JSTaggedValue(std::get(value)); + break; + } + case LiteralTag::BOOL: { + jt = JSTaggedValue(std::get(value)); + break; + } + case LiteralTag::STRING: { + StringData sd = std::get(value); + EcmaString *str = factory->GetRawStringFromStringTable(sd.data, sd.utf16_length); + jt = JSTaggedValue(str); + uint32_t index = 0; + if (JSTaggedValue::ToElementIndex(jt, &index) && ppos % pairSize == 0) { + flag = true; + } + break; + } + case LiteralTag::METHOD: { + ASSERT(pft != nullptr); + uint32_t methodId = std::get(value); + JSHandle jsFunc = pft->DefineMethodById(methodId, FunctionKind::NORMAL_FUNCTION); + jt = jsFunc.GetTaggedValue(); + break; + } + case LiteralTag::GENERATORMETHOD: { + ASSERT(pft != nullptr); + uint32_t methodId = std::get(value); + JSHandle jsFunc = pft->DefineMethodById(methodId, FunctionKind::GENERATOR_FUNCTION); + jt = jsFunc.GetTaggedValue(); + break; + } + case LiteralTag::ACCESSOR: { + JSHandle accessor = factory->NewAccessorData(); + jt = JSTaggedValue(accessor.GetTaggedValue()); + break; + } + case LiteralTag::NULLVALUE: { + break; + } + default: { + UNREACHABLE(); + break; + } + } + if (epos % pairSize == 0 && !flag) { + properties->Set(thread, ppos++, jt); + } else { + elements->Set(thread, epos++, jt); + } + }); +} + +JSHandle LiteralDataExtractor::GetDatasIgnoreType(JSThread *thread, const panda_file::File *pf, + size_t index, PandaFileTranslator *pft) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + LOG_ECMA(DEBUG) << "Panda File" << pf->GetFilename(); + panda_file::File::EntityId literalArraysId = pf->GetLiteralArraysId(); + panda_file::LiteralDataAccessor lda(*pf, literalArraysId); + + uint32_t num = lda.GetLiteralValsNum(index) / 2; // 2: half + JSHandle literals = factory->NewTaggedArray(num); + array_size_t pos = 0; + lda.EnumerateLiteralVals( + index, [literals, &pos, factory, thread, pft](const panda_file::LiteralDataAccessor::LiteralValue &value, + const LiteralTag &tag) { + JSTaggedValue jt = JSTaggedValue::Null(); + switch (tag) { + case LiteralTag::INTEGER: { + jt = JSTaggedValue(std::get(value)); + break; + } + case LiteralTag::DOUBLE: { + jt = JSTaggedValue(std::get(value)); + break; + } + case LiteralTag::BOOL: { + jt = JSTaggedValue(std::get(value)); + break; + } + case LiteralTag::STRING: { + StringData sd = std::get(value); + EcmaString *str = factory->GetRawStringFromStringTable(sd.data, sd.utf16_length); + jt = JSTaggedValue(str); + break; + } + case LiteralTag::METHOD: { + ASSERT(pft != nullptr); + uint32_t methodId = std::get(value); + JSHandle jsFunc = pft->DefineMethodById(methodId, FunctionKind::NORMAL_FUNCTION); + jt = jsFunc.GetTaggedValue(); + break; + } + case LiteralTag::GENERATORMETHOD: { + ASSERT(pft != nullptr); + uint32_t methodId = std::get(value); + JSHandle jsFunc = pft->DefineMethodById(methodId, FunctionKind::GENERATOR_FUNCTION); + jt = jsFunc.GetTaggedValue(); + break; + } + case LiteralTag::ACCESSOR: { + JSHandle accessor = factory->NewAccessorData(); + jt = accessor.GetTaggedValue(); + break; + } + case LiteralTag::NULLVALUE: { + break; + } + default: { + UNREACHABLE(); + break; + } + } + literals->Set(thread, pos++, jt); + }); + return literals; +} +} // namespace panda::ecmascript diff --git a/ecmascript/literal_data_extractor.h b/ecmascript/literal_data_extractor.h new file mode 100644 index 0000000000000000000000000000000000000000..d36fb68184ecec405aa8c9d2e71ce36c907c3129 --- /dev/null +++ b/ecmascript/literal_data_extractor.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_LITERAL_DATA_EXTRACTOR_H +#define PANDA_RUNTIME_ECMASCRIPT_LITERAL_DATA_EXTRACTOR_H + +#include "ecmascript/class_linker/panda_file_translator.h" +#include "ecmascript/js_tagged_value-inl.h" + +namespace panda::ecmascript { +using EntityId = panda_file::File::EntityId; + +enum class FieldTag : uint8_t { OBJECTLITERAL = 0, ARRAYLITERAL }; + +class LiteralDataExtractor { +public: + explicit LiteralDataExtractor() = default; + virtual ~LiteralDataExtractor() = default; + + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(LiteralDataExtractor); + DEFAULT_COPY_SEMANTIC(LiteralDataExtractor); + + static void ExtractObjectDatas(JSThread *thread, const panda_file::File *pf, size_t index, + JSMutableHandle elements, JSMutableHandle properties, + PandaFileTranslator *pft); + static JSHandle GetDatasIgnoreType(JSThread *thread, const panda_file::File *pf, size_t index, + PandaFileTranslator *pft = nullptr); +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_LITERAL_DATA_EXTRACTOR_H diff --git a/ecmascript/mem/allocator-inl.h b/ecmascript/mem/allocator-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..8546afdb76c0adfffb9f1a96c7925a14d550ec42 --- /dev/null +++ b/ecmascript/mem/allocator-inl.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_ALLOCATOR_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_ALLOCATOR_INL_H + +#include + +#include "ecmascript/mem/allocator.h" +#include "ecmascript/mem/heap-inl.h" +#include "ecmascript/mem/space.h" +#include "ecmascript/free_object.h" + +namespace panda::ecmascript { +BumpPointerAllocator::BumpPointerAllocator(const Space *space) + : BumpPointerAllocator(space->GetAllocateAreaBegin(), space->GetAllocateAreaEnd()) +{ +} + +BumpPointerAllocator::BumpPointerAllocator(uintptr_t begin, uintptr_t end) : begin_(begin), top_(begin), end_(end) {} + +void BumpPointerAllocator::Reset() +{ + begin_ = 0; + top_ = 0; + end_ = 0; +} + +void BumpPointerAllocator::Reset(uintptr_t begin, uintptr_t end) +{ + begin_ = begin; + top_ = begin; + end_ = end; +} + +void BumpPointerAllocator::Reset(const Space *space) +{ + Reset(space->GetAllocateAreaBegin(), space->GetAllocateAreaEnd()); +} + +uintptr_t BumpPointerAllocator::Allocate(size_t size) +{ + if (size == 0) { + LOG_ECMA_MEM(FATAL) << "size must have a size bigger than 0"; + UNREACHABLE(); + } + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (UNLIKELY(top_ + size > end_)) { + return 0; + } + uintptr_t result = top_; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + top_ += size; + return result; +} + +FreeListAllocator::FreeListAllocator(const Space *space) +{ + freeList_ = std::make_unique(); + heap_ = space->GetHeap(); + uintptr_t begin = space->GetCurrentRegion()->GetBegin(); + size_t size = space->GetCurrentRegion()->GetSize(); + FreeObject::Cast(begin)->SetAvailable(size); + freeList_->Free(begin, size); +} + +void FreeListAllocator::AddFree(Region *region) +{ + auto begin = region->GetBegin(); + auto end = region->GetEnd(); + Free(begin, end); +} + +uintptr_t FreeListAllocator::Allocate(size_t size) +{ + if (UNLIKELY(size < static_cast(TaggedObject::ObjectHeaderSize()))) { + return 0; + } + auto ret = bpAllocator_.Allocate(size); + if (LIKELY(ret != 0)) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), bpAllocator_.GetTop(), bpAllocator_.Available()); + return ret; + } + FreeObject *object = freeList_->Allocator(size); + if (LIKELY(object != nullptr && !object->IsEmpty())) { + FreeBumpPoint(); + bpAllocator_.Reset(object->GetBegin(), object->GetEnd()); + ret = bpAllocator_.Allocate(size); + if (ret != 0 && bpAllocator_.Available() > 0) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), bpAllocator_.GetTop(), bpAllocator_.Available()); + } + return ret; + } + + return 0; +} + +void FreeListAllocator::FreeBumpPoint() +{ + auto begin = bpAllocator_.GetTop(); + auto end = bpAllocator_.GetEnd(); + Free(begin, end); + bpAllocator_.Reset(); +} + +void FreeListAllocator::Free(uintptr_t begin, uintptr_t end) +{ + ASSERT(heap_ != nullptr); + size_t size = end - begin; + if (UNLIKELY(size < FreeObject::SIZE_OFFSET)) { + return; + } + FreeObject::FillFreeObject(heap_->GetEcmaVM(), begin, size); + + freeList_->Free(begin, static_cast(end - begin)); +} + +void FreeListAllocator::RebuildFreeList() +{ + bpAllocator_.Reset(); + freeList_->Rebuild(); +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_ALLOCATOR_INL_H diff --git a/ecmascript/mem/allocator.h b/ecmascript/mem/allocator.h new file mode 100644 index 0000000000000000000000000000000000000000..38f40bb53f3a4cfb038114ce926edf1578e90521 --- /dev/null +++ b/ecmascript/mem/allocator.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_ALLOCATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_ALLOCATOR_H + +#include + +#include "ecmascript/mem/free_object_list.h" +#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/mem.h" + +namespace panda::ecmascript { +class Space; +class Region; + +class Allocator { +public: + Allocator() = default; + virtual ~Allocator() = default; + NO_COPY_SEMANTIC(Allocator); + NO_MOVE_SEMANTIC(Allocator); +}; + +class BumpPointerAllocator : public Allocator { +public: + BumpPointerAllocator() = default; + ~BumpPointerAllocator() override = default; + NO_COPY_SEMANTIC(BumpPointerAllocator); + NO_MOVE_SEMANTIC(BumpPointerAllocator); + + inline explicit BumpPointerAllocator(const Space *space); + inline BumpPointerAllocator(uintptr_t begin, uintptr_t end); + + inline void Reset(); + inline void Reset(const Space *space); + inline void Reset(uintptr_t begin, uintptr_t end); + inline uintptr_t Allocate(size_t size); + + uintptr_t GetTop() const + { + return top_; + } + + uintptr_t GetEnd() const + { + return end_; + } + + void Swap(const BumpPointerAllocator &other) + { + begin_ = other.begin_; + top_ = other.top_; + end_ = other.end_; + } + + size_t Available() const + { + return (end_ - top_); + } + +private: + uintptr_t begin_{0}; + uintptr_t top_{0}; + uintptr_t end_{0}; +}; + +class FreeListAllocator : public Allocator { +public: + FreeListAllocator() = default; + ~FreeListAllocator() override = default; + + NO_COPY_SEMANTIC(FreeListAllocator); + NO_MOVE_SEMANTIC(FreeListAllocator); + + inline explicit FreeListAllocator(const Space *space); + + inline uintptr_t Allocate(size_t size); + inline void AddFree(Region *region); + + inline void RebuildFreeList(); + + void Swap(FreeListAllocator &other) + { + heap_ = other.heap_; + bpAllocator_.Swap(other.bpAllocator_); + freeList_.swap(other.freeList_); + } + + inline void FreeBumpPoint(); + + inline void Free(uintptr_t begin, uintptr_t end); + inline void SplitFreeObject(FreeObject *current, size_t allocateSize); + + size_t GetAvailableSize() const + { + return freeList_->GetFreeObjectSize(); + } + +private: + BumpPointerAllocator bpAllocator_; + std::unique_ptr freeList_; + Heap *heap_{nullptr}; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_ALLOCATOR_H diff --git a/ecmascript/mem/area.h b/ecmascript/mem/area.h new file mode 100644 index 0000000000000000000000000000000000000000..a520595cbe34925f1ab0574fcf38451395b001ff --- /dev/null +++ b/ecmascript/mem/area.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_AREA_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_AREA_H + +namespace panda::ecmascript { +class Area { +public: + Area(uintptr_t begin, size_t capacity) + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + : begin_(begin), end_(begin + capacity), next_(nullptr), prev_(nullptr) + { + } + ~Area() = default; + NO_COPY_SEMANTIC(Area); + NO_MOVE_SEMANTIC(Area); + + uintptr_t GetBegin() const + { + return begin_; + } + + uintptr_t GetEnd() const + { + return end_; + } + + void LinkPrev(Area *prev) + { + prev_ = prev; + } + + void LinkNext(Area *next) + { + next_ = next; + } + + size_t GetSize() + { + return end_ - begin_; + } + +private: + template + friend class EcmaList; + friend class Worker; + Area *GetPrev() const + { + return prev_; + } + + Area *GetNext() const + { + return next_; + } + uintptr_t begin_; + uintptr_t end_; + Area *next_; + Area *prev_; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_AREA_H diff --git a/ecmascript/mem/assert_scope-inl.h b/ecmascript/mem/assert_scope-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..24960c27130ed5a08700a8f364f1c2e14263ae40 --- /dev/null +++ b/ecmascript/mem/assert_scope-inl.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_ASSERT_SCOPE_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_ASSERT_SCOPE_INL_H + +#include "assert_scope.h" + +namespace panda::ecmascript { +// Thread-local storage for assert data. Default all asserts to "allow". +// NOLINTNEXTLINE(hicpp-signed-bitwise) +static thread_local size_t currentAssertData(~0); + +template +AssertScopeT::AssertScopeT() : oldData_(currentAssertData) +{ + switch (type) { + case AssertType::GARBAGE_COLLECTION_ASSERT: + currentAssertData = AssertGarbageCollectBit::Update(oldData_.value(), isAllow); + break; + case AssertType::HEAP_ALLOC_ASSERT: + currentAssertData = AssertHeapAllocBit::Update(oldData_.value(), isAllow); + break; + default: + break; + } +} + +template +AssertScopeT::~AssertScopeT() +{ + if (!oldData_.has_value()) { + return; + } + + currentAssertData = oldData_.value(); + oldData_.reset(); +} + +// static +template +bool AssertScopeT::IsAllowed() +{ + switch (type) { + case AssertType::GARBAGE_COLLECTION_ASSERT: + return AssertGarbageCollectBit::Decode(currentAssertData); + case AssertType::HEAP_ALLOC_ASSERT: + return AssertHeapAllocBit::Decode(currentAssertData); + default: + return true; + } +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_ASSERT_SCOPE_INL_H \ No newline at end of file diff --git a/ecmascript/mem/assert_scope.h b/ecmascript/mem/assert_scope.h new file mode 100644 index 0000000000000000000000000000000000000000..32988db7cbb884064dede15b9349b30e5a5dd903 --- /dev/null +++ b/ecmascript/mem/assert_scope.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_ASSERT_SCOPE_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_ASSERT_SCOPE_H + +#include + +#include "libpandabase/macros.h" +#include "utils/bit_field.h" + +namespace panda::ecmascript { +using AssertGarbageCollectBit = panda::BitField; +using AssertHeapAllocBit = AssertGarbageCollectBit::NextFlag; + +#ifndef NDEBUG +constexpr bool IS_ALLOW_CHECK = true; +#else +constexpr bool IS_ALLOW_CHECK = false; +#endif + +enum class AssertType : uint8_t { GARBAGE_COLLECTION_ASSERT = 0, HEAP_ALLOC_ASSERT, LAST_ASSERT_TYPE }; + +template +class AssertScopeT { +public: + static bool IsAllowed() + { + return true; + } +}; + +template +class AssertScopeT { +public: + AssertScopeT(); + + ~AssertScopeT(); + + static bool IsAllowed(); + + NO_COPY_SEMANTIC(AssertScopeT); + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(AssertScopeT); + +private: + std::optional oldData_; +}; + +using DisallowGarbageCollection = AssertScopeT; +using AllowGarbageCollection = AssertScopeT; +using DisAllowHeapAlloc = AssertScopeT; +using AllowHeapAlloc = AssertScopeT; + +#if (!defined NDEBUG) || (defined RUN_TEST) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DISALLOW_GARBAGE_COLLECTION [[maybe_unused]] DisallowGarbageCollection no_gc +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ALLOW_GARBAGE_COLLECTION [[maybe_unused]] AllowGarbageCollection allow_gc +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DISALLOW_HEAP_ALLOC [[maybe_unused]] DisAllowHeapAlloc no_heap_alloc +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ALLOW_HEAP_ALLOC [[maybe_unused]] AllowHeapAlloc allow_heap_alloc +#else +#define DISALLOW_GARBAGE_COLLECTION +#define ALLOW_GARBAGE_COLLECTION +#define DISALLOW_HEAP_ALLOC +#define ALLOW_HEAP_ALLOC +#endif + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHECK_NO_GC ASSERT_PRINT(AllowGarbageCollection::IsAllowed(), "disallow execute garbage collection."); + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHECK_NO_HEAP_ALLOC (AllowHeapAlloc::IsAllowed(), "disallow execute heap alloc."); +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_ASSERT_SCOPE_H diff --git a/ecmascript/mem/barriers-inl.h b/ecmascript/mem/barriers-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..1b2cac55f682e3d4a20abd905c846f8622d772ca --- /dev/null +++ b/ecmascript/mem/barriers-inl.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_BARRIERS_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_BARRIERS_INL_H + +#include "ecmascript/mem/barriers.h" +#include "ecmascript/mem/space-inl.h" +#include "ecmascript/mem/mem.h" +#include "ecmascript/mem/region.h" + +namespace panda::ecmascript { +static inline void MarkingBarrier(void *obj, size_t offset, JSTaggedType value) +{ + ASSERT(value != JSTaggedValue::VALUE_UNDEFINED); + Region *object_region = Region::ObjectAddressToRange(static_cast(obj)); + Region *value_region = Region::ObjectAddressToRange(reinterpret_cast(value)); + if (!object_region->InYoungGeneration() && value_region->InYoungGeneration()) { + [[maybe_unused]] uintptr_t slot_addr = ToUintPtr(obj) + offset; + // Should align with '8' in 64 and 32 bit platform + ASSERT((slot_addr % static_cast(MemAlignment::MEM_ALIGN_OBJECT)) == 0); + object_region->InsertOldToNewRememberedSet(slot_addr); + } +} + +/* static */ +// CODECHECK-NOLINTNEXTLINE(C_RULE_ID_COMMENT_LOCATION) +template +inline void Barriers::SetDynObject([[maybe_unused]] const JSThread *thread, void *obj, size_t offset, + JSTaggedType value) +{ + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) + *reinterpret_cast(reinterpret_cast(obj) + offset) = value; + MarkingBarrier(obj, offset, value); +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_BARRIERS_INL_H diff --git a/ecmascript/mem/barriers.h b/ecmascript/mem/barriers.h new file mode 100644 index 0000000000000000000000000000000000000000..6e773b317a12ad6b0b7a07736708f73fd52aa641 --- /dev/null +++ b/ecmascript/mem/barriers.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_BARRIERS_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_BARRIERS_H + +#include "ecmascript/mem/mark_word.h" + +namespace panda::ecmascript { +class Barriers { +public: + template + static inline void SetDynPrimitive(void *obj, size_t offset, T value) + { + auto *addr = reinterpret_cast(ToUintPtr(obj) + offset); + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) + *addr = value; + } + + template + static inline bool AtomicSetDynPrimitive(volatile void *obj, size_t offset, T oldValue, T value) + { + volatile auto atomicField = reinterpret_cast *>(ToUintPtr(obj) + offset); + return std::atomic_compare_exchange_strong_explicit(atomicField, &oldValue, value, std::memory_order_release, + std::memory_order_relaxed); + } + + template + static inline void SetDynObject(const JSThread *thread, void *obj, size_t offset, JSTaggedType value); + + template + static inline T GetDynValue(const void *obj, size_t offset) + { + auto *addr = reinterpret_cast(ToUintPtr(obj) + offset); + return *addr; + } +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_BARRIERS_H diff --git a/ecmascript/mem/c_containers.h b/ecmascript/mem/c_containers.h new file mode 100644 index 0000000000000000000000000000000000000000..d42bed60f0bfb3b392e7cf7298ffed8122ad6a94 --- /dev/null +++ b/ecmascript/mem/c_containers.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBPANDABASE_ECMASCRIPT_MEM_C_CONTAINERS_H +#define LIBPANDABASE_ECMASCRIPT_MEM_C_CONTAINERS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ecmascript/mem/caddress_allocator.h" + +namespace panda::ecmascript { +template +using CVector = std::vector>; + +template +using CList = std::list>; + +template > +using CMap = std::map>>; + +template > +using CMultiMap = std::multimap>>; + +template , class KeyEqual = std::equal_to> +using CUnorderedMultiMap = + std::unordered_multimap>>; + +template +using CDeque = std::deque>; + +template > +using CQueue = std::queue; + +template , class KeyEqual = std::equal_to> +using CUnorderedMap = std::unordered_map>>; + +template , class KeyEqual = std::equal_to> +using CUnorderedSet = std::unordered_set>; +} // namespace panda::ecmascript + +#endif // LIBPANDABASE_ECMASCRIPT_MEM_C_CONTAINERS_H diff --git a/ecmascript/mem/c_string.cpp b/ecmascript/mem/c_string.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1fe5b6b6902066760513151e1445d89dd4f4fd0f --- /dev/null +++ b/ecmascript/mem/c_string.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/c_string.h" + +#include +#include + +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/js_symbol.h" +#include "ecmascript/mem/c_containers.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +constexpr int BASE = 10; + +int64_t CStringToLL(const CString &str) +{ + [[maybe_unused]] char *endPtr = nullptr; + int64_t result = std::strtoll(str.c_str(), &endPtr, BASE); + ASSERT(!(result == 0 && str.c_str() == endPtr) && "CString argument is not long long int"); + return result; +} + +uint64_t CStringToULL(const CString &str) +{ + [[maybe_unused]] char *endPtr = nullptr; + uint64_t result = std::strtoull(str.c_str(), &endPtr, BASE); + ASSERT(!(result == 0 && str.c_str() == endPtr) && "CString argument is not unsigned long long int"); + return result; +} + +float CStringToF(const CString &str) +{ + [[maybe_unused]] char *endPtr = nullptr; + float result = std::strtof(str.c_str(), &endPtr); + ASSERT(result != HUGE_VALF && "CString argument is not float"); + ASSERT(!(result == 0 && str.c_str() == endPtr) && "CString argument is not float"); + return result; +} + +double CStringToD(const CString &str) +{ + [[maybe_unused]] char *endPtr = nullptr; + double result = std::strtod(str.c_str(), &endPtr); + ASSERT(result != HUGE_VALF && "CString argument is not double"); + ASSERT(!(result == 0 && str.c_str() == endPtr) && "CString argument is not double"); + return result; +} + +template +CString ConvertToString(T sp) +{ + CString res; + res.reserve(sp.size()); + + // Also support ascii that great than 127, so using unsigned char here + constexpr size_t maxChar = std::numeric_limits::max(); + + for (const auto &c : sp) { + if (c > maxChar) { + return ""; + } + res.push_back(c); + } + + return res; +} + +// NB! the following function need additional mem allocation, don't use when unnecessary! +CString ConvertToString(const std::string &str) +{ + CString res; + res.reserve(str.size()); + for (auto c : str) { + res.push_back(c); + } + return res; +} + +CString ConvertToString(const EcmaString *s, StringConvertedUsage usage) +{ + if (s == nullptr) { + return CString(""); + } + if (s->IsUtf16()) { + // Should convert utf-16 to utf-8, because uint16_t likely greater than maxChar, will convert fail + bool modify = (usage != StringConvertedUsage::PRINT); + size_t len = base::utf_helper::Utf16ToUtf8Size(s->GetDataUtf16(), s->GetLength(), modify) - 1; + CVector buf(len); + len = base::utf_helper::ConvertRegionUtf16ToUtf8(s->GetDataUtf16(), buf.data(), s->GetLength(), len, 0, modify); + Span sp(buf.data(), len); + return ConvertToString(sp); + } + + Span sp(s->GetDataUtf8(), s->GetLength()); + return ConvertToString(sp); +} + +CString ConvertToString(JSTaggedValue key) +{ + ASSERT(key.IsStringOrSymbol()); + if (key.IsString()) { + return ConvertToString(EcmaString::ConstCast(key.GetTaggedObject())); + } + + ecmascript::JSTaggedValue desc = JSSymbol::Cast(key.GetTaggedObject())->GetDescription(); + if (desc.IsUndefined()) { + return CString("Symbol()"); + } + + return ConvertToString(EcmaString::ConstCast(desc.GetTaggedObject())); +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/c_string.h b/ecmascript/mem/c_string.h new file mode 100644 index 0000000000000000000000000000000000000000..f565ebe48f8c1db7b1d7dc420add0e0c6bae8dd4 --- /dev/null +++ b/ecmascript/mem/c_string.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_C_STRING_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_C_STRING_H + +#include +#include +#include + +#include "ecmascript/common.h" +#include "ecmascript/mem/caddress_allocator.h" + +namespace panda::ecmascript { +class EcmaString; +class JSTaggedValue; + +using CString = std::basic_string, CAddressAllocator>; +using CStringStream = std::basic_stringstream, CAddressAllocator>; + +// PRINT will skip '\0' in utf16 during conversion of utf8 +enum StringConvertedUsage { PRINT, LOGICOPERATION }; + +int64_t CStringToLL(const CString &str); +uint64_t CStringToULL(const CString &str); +float CStringToF(const CString &str); +double CStringToD(const CString &str); + +CString ConvertToString(const std::string &str); + +// '\u0000' is skip according to holdZero +CString ConvertToString(const ecmascript::EcmaString *s, StringConvertedUsage usage = StringConvertedUsage::PRINT); +CString ConvertToString(ecmascript::JSTaggedValue key); + +template +std::enable_if_t, CString> FloatToCString(T number) +{ + CStringStream strStream; + strStream << number; + return strStream.str(); +} + +template +std::enable_if_t, CString> ToCString(T number) +{ + if (number == 0) { + return CString("0"); + } + bool IsNeg = false; + if (number < 0) { + number = -number; + IsNeg = true; + } + + static constexpr uint32_t BUFF_SIZE = std::numeric_limits::digits10 + 3; // 3: Reserved for sign bit and '\0'. + char buf[BUFF_SIZE]; + uint32_t position = BUFF_SIZE - 1; + buf[position] = '\0'; + while (number > 0) { + buf[--position] = number % 10 + '0'; // 10 : decimal + number /= 10; // 10 : decimal + } + if (IsNeg) { + buf[--position] = '-'; + } + return CString(&buf[position]); +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_C_STRING_H diff --git a/ecmascript/mem/caddress_allocator.h b/ecmascript/mem/caddress_allocator.h new file mode 100644 index 0000000000000000000000000000000000000000..7c522ae48d0cf1af93d41590f6eb159ff27584a5 --- /dev/null +++ b/ecmascript/mem/caddress_allocator.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_ECMASCRIPT_C_ADDRESS_ALLOCATOR_H +#define RUNTIME_ECMASCRIPT_C_ADDRESS_ALLOCATOR_H + +#include "ecmascript/mem/chunk.h" + +namespace panda::ecmascript { +template +class CAddressAllocator { +public: + // using by std allocator + using value_type = T; + using pointer = T *; + using reference = T &; + using const_pointer = const T *; + using const_reference = const T &; + using size_type = size_t; + using difference_type = ptrdiff_t; + + template + struct Rebind { + using other = CAddressAllocator; + }; + + template + using rebind = Rebind; + + CAddressAllocator() = default; + + template + explicit CAddressAllocator(const CAddressAllocator &other [[maybe_unused]]) + { + } + + CAddressAllocator(const CAddressAllocator &) = default; + CAddressAllocator &operator=(const CAddressAllocator &) = default; + CAddressAllocator(CAddressAllocator &&other) noexcept = default; + CAddressAllocator &operator=(CAddressAllocator &&other) noexcept = default; + ~CAddressAllocator() = default; + + // NOLINTNEXTLINE(readability-identifier-naming) + size_type max_size() const + { + return static_cast(-1) / sizeof(T); + } + + bool operator==([[maybe_unused]] CAddressAllocator const &other) const + { + return false; + } + + bool operator!=([[maybe_unused]] CAddressAllocator const &other) const + { + return true; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + pointer allocate(size_type n, [[maybe_unused]] const void *ptr = nullptr) + { + ASSERT(n <= max_size()); + return static_cast(Allocate(n * sizeof(T))); + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void deallocate([[maybe_unused]] pointer p, [[maybe_unused]] size_type n) + { + Free(static_cast(p)); + } + + template + void construct(U *p, Args &&... args) // NOLINT(readability-identifier-naming) + { + if (p == nullptr) { + return; + } + ::new (static_cast(p)) U(std::forward(args)...); + } + template + void destroy(U *p) // NOLINT(readability-identifier-naming) + { + if (p == nullptr) { + return; + } + p->~U(); + } + + [[nodiscard]] void *Allocate(size_t size) + { + if (size == 0) { + LOG_ECMA_MEM(FATAL) << "size must have a size bigger than 0"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + void *ptr = malloc(size); + if (ptr == nullptr) { + LOG_ECMA_MEM(FATAL) << "malloc failed"; + UNREACHABLE(); + } + return ptr; + } + + template + [[nodiscard]] S *New(Args &&... args) + { + auto p = reinterpret_cast(Allocate(sizeof(S))); + new (p) S(std::forward(args)...); + return reinterpret_cast(p); + } + + template + void Finalize(S *ptr) + { + ASSERT(ptr != nullptr); + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + if constexpr (std::is_class_v) { + ptr->~S(); + } + Free(ptr); + } + + [[nodiscard]] T *AllocArray(size_t size) + { + return static_cast(Allocate(size * sizeof(T))); + } + + void Delete(T *ptr) + { + if (ptr == nullptr) { + return; + } + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + if constexpr (std::is_class_v) { + ptr->~T(); + } + Free(ptr); + } + + void Free(void *mem) + { + if (mem == nullptr) { + return; + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + os::memory::LockHolder lock(staticResourceLock_); + free(mem); + } +}; +} // namespace panda::ecmascript + +#endif // RUNTIME_ECMASCRIPT_C_ADDRESS_ALLOCATOR_H \ No newline at end of file diff --git a/ecmascript/mem/chunk.cpp b/ecmascript/mem/chunk.cpp new file mode 100644 index 0000000000000000000000000000000000000000..db213ea3f7c04f1d0c5b891d96d14f8042a44153 --- /dev/null +++ b/ecmascript/mem/chunk.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/chunk.h" + +#include "ecmascript/mem/heap.h" + +namespace panda::ecmascript { +Chunk::Chunk(RegionFactory *factory) : factory_(factory) {} + +Area *Chunk::NewArea(size_t size) +{ + auto area = factory_->AllocateArea(size); + if (area == nullptr) { + LOG_ECMA_MEM(FATAL) << "OOM Chunk::NewArea area is nullptr"; + UNREACHABLE(); + } + areaList_.AddNode(area); + currentArea_ = area; + return area; +} + +uintptr_t Chunk::Expand(size_t size) +{ + ASSERT(end_ - ptr_ < size); + + Area *head = currentArea_; + size_t newSize; + if (head != nullptr) { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + newSize = size + (head->GetSize() << 1); + } else { + newSize = sizeof(Area) + MEM_ALIGN + size; + } + + if (newSize < MIN_CHUNK_AREA_SIZE) { + newSize = MIN_CHUNK_AREA_SIZE; + } else if (newSize > MAX_CHUNK_AREA_SIZE) { + size_t minNewSize = sizeof(Area) + MEM_ALIGN + size; + newSize = std::max(minNewSize, MAX_CHUNK_AREA_SIZE); + } + + if (newSize > static_cast(std::numeric_limits::max())) { + LOG_ECMA_MEM(FATAL) << "OOM chunk"; + UNREACHABLE(); + } + + Area *area = NewArea(newSize); + if (area == nullptr) { + LOG_ECMA_MEM(FATAL) << "OOM chunk"; + UNREACHABLE(); + } + uintptr_t result = AlignUp(area->GetBegin(), MEM_ALIGN); + ptr_ = result + size; + end_ = area->GetEnd(); + return result; +} + +void Chunk::ReleaseMemory() +{ + while (!areaList_.IsEmpty()) { + Area *node = areaList_.PopBack(); + factory_->FreeArea(node); + } + ptr_ = 0; + end_ = 0; +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/chunk.h b/ecmascript/mem/chunk.h new file mode 100644 index 0000000000000000000000000000000000000000..1af052bf98c219d255379054adb49cc8cb72d82b --- /dev/null +++ b/ecmascript/mem/chunk.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_ECMASCRIPT_CHUNK_H +#define RUNTIME_ECMASCRIPT_CHUNK_H + +#include "ecmascript/mem/ecma_list.h" +#include "ecmascript/mem/area.h" + +namespace panda::ecmascript { +class RegionFactory; + +class Chunk { +public: + static constexpr size_t MEM_ALIGN = 8U; + + explicit Chunk(RegionFactory *factory); + ~Chunk() + { + ReleaseMemory(); + } + + NO_COPY_SEMANTIC(Chunk); + NO_MOVE_SEMANTIC(Chunk); + + [[nodiscard]] void *Allocate(size_t size) + { + if (size == 0) { + LOG_ECMA_MEM(FATAL) << "size must have a size bigger than 0"; + UNREACHABLE(); + } + uintptr_t result = ptr_; + size = AlignUp(size, MEM_ALIGN); + if (UNLIKELY(size > end_ - ptr_)) { + result = Expand(size); + } else { + ptr_ += size; + } + + return reinterpret_cast(result); + } + + template + [[nodiscard]] T *NewArray(size_t size) + { + return static_cast(Allocate(size * sizeof(T))); + } + + template + [[nodiscard]] T *New(Args &&... args) + { + auto p = reinterpret_cast(Allocate(sizeof(T))); + new (p) T(std::forward(args)...); + return reinterpret_cast(p); + } + + template + void Delete(T *ptr) + { + ASSERT(ptr != nullptr); + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + if constexpr (std::is_class_v) { + ptr->~T(); + } + Free(ptr); + } + + void Free([[maybe_unused]] void *mem) + { + // do nothing + } + +private: + uintptr_t Expand(size_t size); + Area *NewArea(size_t size); + void ReleaseMemory(); + + uintptr_t ptr_{0}; + uintptr_t end_{0}; + + Area *currentArea_{nullptr}; + EcmaList areaList_{}; + RegionFactory *factory_{nullptr}; +}; +} // namespace panda::ecmascript + +#endif // RUNTIME_ECMASCRIPT_CHUNK_H diff --git a/ecmascript/mem/chunk_allocator.h b/ecmascript/mem/chunk_allocator.h new file mode 100644 index 0000000000000000000000000000000000000000..886c05f272aad969429d8e69438c58ad7f323b2d --- /dev/null +++ b/ecmascript/mem/chunk_allocator.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RUNTIME_ECMASCRIPT_CHUNK_ALLOCATOR_H +#define RUNTIME_ECMASCRIPT_CHUNK_ALLOCATOR_H + +#include "ecmascript/mem/chunk.h" + +namespace panda::ecmascript { +template +class ChunkAllocator { +public: + // used for std allocator + using value_type = T; + using pointer = T *; + using reference = T &; + using const_pointer = const T *; + using const_reference = const T &; + using size_type = size_t; + using difference_type = ptrdiff_t; + + template + struct Rebind { + using other = ChunkAllocator; + }; + + template + using rebind = Rebind; + + explicit ChunkAllocator(Chunk *chunk) : chunk_(chunk) {} + + template + ChunkAllocator(const ChunkAllocator &other) : chunk_(other.chunk_) + { + } + template + friend class ChunkAllocator; + + ChunkAllocator(const ChunkAllocator &) = default; + ChunkAllocator &operator=(const ChunkAllocator &) = default; + ChunkAllocator(ChunkAllocator &&other) noexcept + { + chunk_ = other.chunk_; + other.chunk_ = nullptr; + } + ChunkAllocator &operator=(ChunkAllocator &&other) noexcept + { + chunk_ = other.chunk_; + other.chunk_ = nullptr; + return *this; + } + ~ChunkAllocator() = default; + + // NOLINTNEXTLINE(readability-identifier-naming) + size_type max_size() const + { + return static_cast(-1) / sizeof(T); + } + + // NOLINTNEXTLINE(readability-identifier-naming) + pointer address(reference x) const + { + return &x; + } + // NOLINTNEXTLINE(readability-identifier-naming) + const_pointer address(const_reference x) const + { + return &x; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + pointer allocate(size_type n, [[maybe_unused]] const void *ptr = nullptr) + { + ASSERT(n <= max_size()); + return chunk_->NewArray(n); + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void deallocate([[maybe_unused]] pointer p, [[maybe_unused]] size_type n) {} + + template + void construct(U *p, Args &&... args) // NOLINT(readability-identifier-naming) + { + ::new (static_cast(p)) U(std::forward(args)...); + } + template + void destroy(U *p) // NOLINT(readability-identifier-naming) + { + if (p == nullptr) { + return; + } + p->~U(); + } + + bool operator==(ChunkAllocator const &other) const + { + return chunk_ == other.chunk_; + } + bool operator!=(ChunkAllocator const &other) const + { + return chunk_ != other.chunk_; + } + + [[nodiscard]] void *Alloc(size_t size) + { + return chunk_->NewArray(size); + } + + [[nodiscard]] T *AllocArray(size_t size) + { + return chunk_->NewArray(size); + } + + void Delete(T *ptr) + { + if (ptr == nullptr) { + LOG_ECMA_MEM(FATAL) << "free nullptr"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + if constexpr (std::is_class_v) { + ptr->~T(); + } + Free(ptr); + } + + void Free([[maybe_unused]] void *mem) + { + } + + Chunk *chunk() + { + return chunk_; + } + +private: + Chunk *chunk_; +}; +} // namespace panda::ecmascript + +#endif // RUNTIME_ECMASCRIPT_CHUNK_ALLOCATOR_H diff --git a/ecmascript/mem/chunk_containers.h b/ecmascript/mem/chunk_containers.h new file mode 100644 index 0000000000000000000000000000000000000000..2bf7ae4d9f5562caa047992cc859e7c2e1ab7562 --- /dev/null +++ b/ecmascript/mem/chunk_containers.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBPANDABASE_ECMASCRIPT_MEM_CONTAINERS_H +#define LIBPANDABASE_ECMASCRIPT_MEM_CONTAINERS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ecmascript/mem/chunk_allocator.h" + +namespace panda::ecmascript { +template +class ChunkVector : public std::vector> { +public: + explicit ChunkVector(Chunk *chunk) : std::vector>(ChunkAllocator(chunk)) {} + + ChunkVector(size_t size, Chunk *chunk) : std::vector>(size, T(), ChunkAllocator(chunk)) {} + + ChunkVector(size_t size, T def, Chunk *chunk) + : std::vector>(size, def, ChunkAllocator(chunk)) + { + } + ~ChunkVector() = default; + NO_COPY_SEMANTIC(ChunkVector); + NO_MOVE_SEMANTIC(ChunkVector); +}; + +template > +class ChunkMap : public std::map>> { +public: + // Constructs an empty map. + explicit ChunkMap(Chunk *chunk) + : std::map>>(Compare(), + ChunkAllocator>(chunk)) + { + } + ~ChunkMap() = default; + NO_COPY_SEMANTIC(ChunkMap); + NO_MOVE_SEMANTIC(ChunkMap); +}; + +template , typename KeyEqual = std::equal_to> +class ChunkUnorderedMap : public std::unordered_map>> { +public: + // NOLINTNEXTLINE(readability-magic-numbers) + explicit ChunkUnorderedMap(Chunk *chunk, size_t bucket_count = 100) + : std::unordered_map>>( + bucket_count, Hash(), KeyEqual(), ChunkAllocator>(chunk)) + { + } + ~ChunkUnorderedMap() = default; + NO_COPY_SEMANTIC(ChunkUnorderedMap); + NO_MOVE_SEMANTIC(ChunkUnorderedMap); +}; + +template > +class ChunkMultimap : public std::multimap>> { +public: + // Constructs an empty multimap. + explicit ChunkMultimap(Chunk *chunk) + : std::multimap>>( + Compare(), ChunkAllocator>(chunk)) + { + } + ~ChunkMultimap() = default; + NO_COPY_SEMANTIC(ChunkMultimap); + NO_MOVE_SEMANTIC(ChunkMultimap); +}; +} // namespace panda::ecmascript + +#endif // LIBPANDABASE_ECMASCRIPT_MEM_CONTAINERS_H diff --git a/ecmascript/mem/clock_scope.h b/ecmascript/mem/clock_scope.h new file mode 100644 index 0000000000000000000000000000000000000000..0f8233a42540731bdc3b764c173e81b2949cefd4 --- /dev/null +++ b/ecmascript/mem/clock_scope.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_CLOCK_SCOPE_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_CLOCK_SCOPE_H + +#include "time.h" +#include "chrono" +#include "libpandabase/utils/logger.h" + +namespace panda::ecmascript { +class ClockScope { +using Clock = std::chrono::high_resolution_clock; +using Duration = std::chrono::duration; + +public: + explicit ClockScope(std::string str) : str_(str) + { + start_ = Clock::now(); + } + + Duration GetPauseTime() const + { + return Clock::now() - start_; + } + + ~ClockScope() + { + end_ = Clock::now(); + auto duration = std::chrono::duration_cast(end_ - start_); + LOG(DEBUG, RUNTIME) << str_ << " spent time: " << (float) duration.count() / MILLION_TIME << "ms"; + } + + NO_COPY_SEMANTIC(ClockScope); + NO_MOVE_SEMANTIC(ClockScope); + +private: + std::string str_; + Clock::time_point start_; + Clock::time_point end_; + + static constexpr uint32_t MILLION_TIME = 1000; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_CLOCK_SCOPE_H diff --git a/ecmascript/mem/code.h b/ecmascript/mem/code.h new file mode 100644 index 0000000000000000000000000000000000000000..d5451a9936261a9c8a8f2c4216ebd1ddfaa9d369 --- /dev/null +++ b/ecmascript/mem/code.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_CODE_H +#define PANDA_RUNTIME_ECMASCRIPT_CODE_H + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/mem/tagged_object.h" + +namespace panda { +namespace ecmascript { +class Code : public TaggedObject { +public: + Code(); + ~Code(); + Code(const Code &) = delete; + Code(Code &&) = delete; + Code &operator=(const Code &) = delete; + Code &operator=(Code &&) = delete; + static Code *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsCode()); + return static_cast(object); + } + static constexpr size_t LENGTH_OFFSET = TaggedObject::ObjectHeaderSize(); + + ACCESSORS(length, LENGTH_OFFSET, DATA_OFFSET); + ACCESSORS(data, DATA_OFFSET, SIZE); +}; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_CODE_H diff --git a/ecmascript/mem/compress_collector.cpp b/ecmascript/mem/compress_collector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2bf001dfc06265d711e99f45ff128dc8ad35755e --- /dev/null +++ b/ecmascript/mem/compress_collector.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/mem/clock_scope.h" +#include "ecmascript/mem/compress_collector.h" +#include "ecmascript/mem/compress_gc_marker-inl.h" +#include "ecmascript/mem/ecma_heap_manager.h" +#include "ecmascript/mem/heap-inl.h" +#include "ecmascript/mem/heap_roots-inl.h" +#include "ecmascript/mem/mark_stack.h" +#include "ecmascript/mem/mem.h" + +namespace panda::ecmascript { +CompressCollector::CompressCollector(Heap *heap, bool parallelGc) + : heap_(heap), paralledGC_(parallelGc), marker_(this), rootManager_(heap->GetEcmaVM()) +{ + workList_ = new CompressGCWorker(heap_, heap_->GetThreadPool()->GetThreadNum()); +} + +CompressCollector::~CompressCollector() +{ + if (workList_ != nullptr) { + delete workList_; + workList_ = nullptr; + } +} + +void CompressCollector::RunPhases() +{ + trace::ScopedTrace scoped_trace("CompressCollector::RunPhases"); + [[maybe_unused]] ClockScope clock("CompressCollector::RunPhases"); + InitializePhase(); + MarkingPhase(); + SweepPhases(); + FinishPhase(); + heap_->GetEcmaVM()->GetEcmaGCStats()->StatisticCompressCollector(clock.GetPauseTime(), youngAndOldAliveSize_, + youngSpaceCommitSize_, oldSpaceCommitSize_, + nonMoveSpaceFreeSize_, nonMoveSpaceCommitSize_); +} + +void CompressCollector::InitializePhase() +{ + heap_->GetThreadPool()->WaitTaskFinish(); + auto compressSpace = const_cast(heap_->GetCompressSpace()); + if (compressSpace->GetCommittedSize() == 0) { + compressSpace->SetUp(); + } + auto fromSpace = const_cast(heap_->GetFromSpace()); + if (fromSpace->GetCommittedSize() == 0) { + heap_->InitializeFromSpace(); + } + + FreeListAllocator compressAllocator(compressSpace); + oldSpaceAllocator_.Swap(compressAllocator); + fromSpaceAllocator_.Reset(fromSpace); + auto heapManager = heap_->GetHeapManager(); + nonMovableAllocator_.Swap(heapManager->GetNonMovableSpaceAllocator()); + + auto callback = [](Region *current) { + // ensure mark bitmap + auto bitmap = current->GetMarkBitmap(); + if (bitmap == nullptr) { + current->GetOrCreateMarkBitmap(); + } else { + bitmap->ClearAllBits(); + } + auto rememberset = current->GetOldToNewRememberedSet(); + if (rememberset != nullptr) { + rememberset->ClearAllBits(); + } + }; + heap_->GetNonMovableSpace()->EnumerateRegions(callback); + heap_->GetSnapShotSpace()->EnumerateRegions(callback); + heap_->GetLargeObjectSpace()->EnumerateRegions(callback); + + heap_->FlipCompressSpace(); + heap_->FlipNewSpace(); + workList_->Initialize(); + youngAndOldAliveSize_ = 0; + nonMoveSpaceFreeSize_ = 0; + youngSpaceCommitSize_ = heap_->GetFromSpace()->GetCommittedSize(); + oldSpaceCommitSize_ = heap_->GetCompressSpace()->GetCommittedSize(); + nonMoveSpaceCommitSize_ = heap_->GetNonMovableSpace()->GetCommittedSize(); +} + +void CompressCollector::FinishPhase() +{ + // swap + if (paralledGC_) { + heap_->GetThreadPool()->Submit([this]([[maybe_unused]] uint32_t threadId) -> bool { + const_cast(heap_->GetCompressSpace())->ReclaimRegions(); + const_cast(heap_->GetFromSpace())->ReclaimRegions(); + return true; + }); + } else { + const_cast(heap_->GetCompressSpace())->ReclaimRegions(); + const_cast(heap_->GetFromSpace())->ReclaimRegions(); + } + workList_->Finish(youngAndOldAliveSize_); + auto heapManager = heap_->GetHeapManager(); + heapManager->GetOldSpaceAllocator().Swap(oldSpaceAllocator_); + heapManager->GetNonMovableSpaceAllocator().Swap(nonMovableAllocator_); + heapManager->GetNewSpaceAllocator().Swap(fromSpaceAllocator_); +} + +void CompressCollector::MarkingPhase() +{ + trace::ScopedTrace scoped_trace("MarkingPhase::SweepPhases"); + RootVisitor gcMarkYoung = [this]([[maybe_unused]] Root type, ObjectSlot slot) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + marker_.MarkObject(0, value.GetTaggedObject(), slot); + } + }; + RootRangeVisitor gcMarkRangeYoung = [this]([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + marker_.MarkObject(0, value.GetTaggedObject(), slot); + } + } + }; + rootManager_.VisitVMRoots(gcMarkYoung, gcMarkRangeYoung); + + ProcessMarkStack(0); + if (paralledGC_) { + heap_->GetThreadPool()->WaitTaskFinish(); + } +} + +void CompressCollector::ProcessMarkStack(uint32_t threadId) +{ + while (true) { + TaggedObject *obj = nullptr; + if (!workList_->Pop(threadId, &obj)) { + break; + } + auto jsHclass = obj->GetClass(); + ObjectSlot objectSlot(ToUintPtr(obj)); + // mark dynClass + marker_.MarkObject(threadId, jsHclass, objectSlot); + + rootManager_.MarkObjectBody( + obj, jsHclass, [this, threadId]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsWeak()) { + RecordWeakReference(threadId, reinterpret_cast(slot.SlotAddress())); + continue; + } + if (value.IsHeapObject()) { + marker_.MarkObject(threadId, value.GetTaggedObject(), slot); + } + } + }); + } +} + +void CompressCollector::SweepPhases() +{ + trace::ScopedTrace scoped_trace("CompressCollector::SweepPhases"); + // process weak reference + for (uint32_t i = 0; i < heap_->GetThreadPool()->GetThreadNum(); i++) { + ProcessQueue *queue = workList_->GetWeakReferenceQueue(i); + + while (true) { + auto obj = queue->PopBack(); + if (UNLIKELY(obj == nullptr)) { + break; + } + ObjectSlot slot(ToUintPtr(obj)); + JSTaggedValue value(slot.GetTaggedType()); + auto header = value.GetTaggedWeakRef(); + + Region *objectRegion = Region::ObjectAddressToRange(header); + if (!objectRegion->InYoungAndOldGeneration()) { + auto markBitmap = objectRegion->GetMarkBitmap(); + if (!markBitmap->Test(header)) { + slot.Update(static_cast(JSTaggedValue::Undefined().GetRawData())); + } + } else { + MarkWord markWord(header); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + auto weakRef = JSTaggedValue(JSTaggedValue(dst).CreateAndGetWeakRef()).GetRawTaggedObject(); + slot.Update(weakRef); + } else { + slot.Update(static_cast(JSTaggedValue::Undefined().GetRawData())); + } + } + } + } + + auto stringTable = heap_->GetEcmaVM()->GetEcmaStringTable(); + WeakRootVisitor gcUpdateWeak = [](TaggedObject *header) { + Region *objectRegion = Region::ObjectAddressToRange(header); + if (!objectRegion->InYoungAndOldGeneration()) { + auto markBitmap = objectRegion->GetMarkBitmap(); + if (markBitmap->Test(header)) { + return header; + } + return reinterpret_cast(ToUintPtr(nullptr)); + } + + MarkWord markWord(header); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + return dst; + } + return reinterpret_cast(ToUintPtr(nullptr)); + }; + stringTable->SweepWeakReference(gcUpdateWeak); + heap_->GetEcmaVM()->ProcessReferences(gcUpdateWeak); + + SweepSpace(const_cast(heap_->GetNonMovableSpace()), nonMovableAllocator_); + SweepSpace(heap_->GetLargeObjectSpace()); +} + +void CompressCollector::SweepSpace(LargeObjectSpace *space) +{ + Region *currentRegion = space->GetRegionList().GetFirst(); + while (currentRegion != nullptr) { + Region *next = currentRegion->GetNext(); + auto markBitmap = currentRegion->GetMarkBitmap(); + bool isMarked = false; + markBitmap->IterateOverMarkedChunks([&isMarked]([[maybe_unused]] void *mem) { isMarked = true; }); + if (!isMarked) { + space->GetRegionList().RemoveNode(currentRegion); + space->ClearAndFreeRegion(currentRegion); + } + currentRegion = next; + } +} + +void CompressCollector::SweepSpace(Space *space, FreeListAllocator &allocator) +{ + allocator.RebuildFreeList(); + space->EnumerateRegions([this, &allocator](Region *current) { + auto markBitmap = current->GetMarkBitmap(); + ASSERT(markBitmap != nullptr); + uintptr_t freeStart = current->GetBegin(); + markBitmap->IterateOverMarkedChunks([this, ¤t, &freeStart, &allocator](void *mem) { + ASSERT(current->InRange(ToUintPtr(mem))); + auto header = reinterpret_cast(mem); + auto klass = header->GetClass(); + JSType jsType = klass->GetObjectType(); + auto size = klass->SizeFromJSHClass(jsType, header); + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + + uintptr_t freeEnd = ToUintPtr(mem); + if (freeStart != freeEnd) { + FreeLiveRange(allocator, current, freeStart, freeEnd); + } + freeStart = freeEnd + size; + }); + uintptr_t freeEnd = current->GetEnd(); + if (freeStart != freeEnd) { + FreeLiveRange(allocator, current, freeStart, freeEnd); + } + }); +} + +void CompressCollector::FreeLiveRange(FreeListAllocator &allocator, Region *current, uintptr_t freeStart, + uintptr_t freeEnd) +{ + allocator.Free(freeStart, freeEnd); + nonMoveSpaceFreeSize_ += freeEnd - freeStart; + heap_->ClearSlotsRange(current, freeStart, freeEnd); +} + +uintptr_t CompressCollector::AllocateOld(size_t size) +{ + os::memory::LockHolder lock(mtx_); + uintptr_t result = oldSpaceAllocator_.Allocate(size); + if (UNLIKELY(result == 0)) { + if (!heap_->FillOldSpaceAndTryGC(&oldSpaceAllocator_, false)) { + return 0; + } + result = oldSpaceAllocator_.Allocate(size); + if (UNLIKELY(result == 0)) { + return 0; + } + } + return result; +} + +void CompressCollector::RecordWeakReference(uint32_t threadId, JSTaggedType *ref) +{ + workList_->PushWeakReference(threadId, ref); +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/compress_collector.h b/ecmascript/mem/compress_collector.h new file mode 100644 index 0000000000000000000000000000000000000000..1f31353f956e3c52af82e207e04c252d2f2affeb --- /dev/null +++ b/ecmascript/mem/compress_collector.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_COMPRESS_COLLECTOR_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_COMPRESS_COLLECTOR_H + +#include "ecmascript/mem/compress_gc_marker.h" +#include "ecmascript/mem/semi_space_collector.h" +#include "ecmascript/mem/semi_space_worker.h" + +namespace panda { +namespace ecmascript { +class Heap; +class JSHClass; + +class CompressCollector : public GarbageCollector { +public: + explicit CompressCollector(Heap *heap, bool parallelGc); + ~CompressCollector() override; + + NO_COPY_SEMANTIC(CompressCollector); + NO_MOVE_SEMANTIC(CompressCollector); + + void RunPhases(); + + Heap *GetHeap() const + { + return heap_; + } + +private: + void InitializePhase(); + void MarkingPhase(); + void SweepPhases(); + void FinishPhase(); + void ProcessMarkStack(uint32_t threadId); + + uintptr_t AllocateOld(size_t size); + void RecordWeakReference(uint32_t threadId, JSTaggedType *ref); + void SweepSpace(Space *space, FreeListAllocator &allocator); + void SweepSpace(LargeObjectSpace *space); // Only sweep large space. + void FreeLiveRange(FreeListAllocator &allocator, Region *current, uintptr_t freeStart, uintptr_t freeEnd); + + Heap *heap_; + bool paralledGC_; + CompressGCMarker marker_; + HeapRootManager rootManager_; + CompressGCWorker *workList_; + os::memory::Mutex mtx_; + BumpPointerAllocator fromSpaceAllocator_{}; + FreeListAllocator oldSpaceAllocator_{}; + FreeListAllocator nonMovableAllocator_{}; + + size_t youngAndOldAliveSize_ = 0; + size_t nonMoveSpaceFreeSize_ = 0; + size_t youngSpaceCommitSize_ = 0; + size_t oldSpaceCommitSize_ = 0; + size_t nonMoveSpaceCommitSize_ = 0; + + friend class TlabAllocator; + friend class CompressGCMarker; + friend class CompressGCWorker; +}; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_COMPRESS_COLLECTOR_H diff --git a/ecmascript/mem/compress_gc_marker-inl.h b/ecmascript/mem/compress_gc_marker-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..28372501a8515cd16ecf862ea04ac03d00ea27ac --- /dev/null +++ b/ecmascript/mem/compress_gc_marker-inl.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_COMPRESS_GC_MARKER_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_COMPRESS_GC_MARKER_INL_H + +#include "ecmascript/mem/compress_collector.h" +#include "ecmascript/mem/compress_gc_marker.h" +#include "ecmascript/mem/mark_word.h" +#include "ecmascript/mem/tlab_allocator-inl.h" + +namespace panda::ecmascript { +static constexpr int HEAD_SIZE = TaggedObject::ObjectHeaderSize(); + +inline CompressGCMarker::CompressGCMarker(CompressCollector *compressCollector) : collector_(compressCollector) {} + +inline void CompressGCMarker::MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot) +{ + Region *objectRegion = Region::ObjectAddressToRange(object); + if (!objectRegion->InYoungAndOldGeneration()) { + auto markBitmap = objectRegion->GetMarkBitmap(); + if (!markBitmap->AtomicTestAndSet(object)) { + collector_->workList_->Push(threadId, object); + } + return; + } + + MarkWord markWord(object); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + slot.Update(dst); + return; + } + return EvacuateObject(threadId, object, markWord, slot); +} + +inline void CompressGCMarker::EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord, + ObjectSlot slot) +{ + auto klass = markWord.GetJSHClass(); + JSType jsType = klass->GetObjectType(); + auto size = klass->SizeFromJSHClass(jsType, object); + uintptr_t forwardAddress = collector_->workList_->GetTlabAllocator(threadId)->Allocate(size, SpaceAlloc::OLD_SPACE); + if (UNLIKELY(forwardAddress == 0)) { + LOG_ECMA_MEM(FATAL) << "alloc failed"; + UNREACHABLE(); + } + + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage, hicpp-signed-bitwise) + bool result = Barriers::AtomicSetDynPrimitive(object, 0, markWord.GetValue(), + MarkWord::FromForwardingAddress(forwardAddress)); + if (result) { + CopyObjectWithoutHeader(object, forwardAddress, size); + collector_->workList_->AddAliveSize(threadId, size); + *reinterpret_cast(forwardAddress) = markWord.GetValue(); + if (klass->HasReferenceField(jsType)) { + collector_->workList_->Push(threadId, reinterpret_cast(forwardAddress)); + } + slot.Update(reinterpret_cast(forwardAddress)); + return; + } + FreeObject::FillFreeObject(collector_->heap_->GetEcmaVM(), forwardAddress, + AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT))); + auto dst = MarkWord(object).ToForwardingAddress(); + slot.Update(dst); +} + +inline void CompressGCMarker::CopyObjectWithoutHeader(TaggedObject *object, uintptr_t address, size_t size) +{ + if (memcpy_s(ToVoidPtr(address + HEAD_SIZE), size - HEAD_SIZE, ToVoidPtr(ToUintPtr(object) + HEAD_SIZE), + size - HEAD_SIZE) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_COMPRESS_GC_MARKER_INL_H diff --git a/ecmascript/mem/compress_gc_marker.h b/ecmascript/mem/compress_gc_marker.h new file mode 100644 index 0000000000000000000000000000000000000000..d3f70fc3f72fac1dc297558f8a3df8df33fb74b5 --- /dev/null +++ b/ecmascript/mem/compress_gc_marker.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_COMPRESS_GC_MARKER_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_COMPRESS_GC_MARKER_H + +#include "ecmascript/mem/slots.h" + +namespace panda::ecmascript { +class JSHClass; +class CompressCollector; + +class CompressGCMarker { +public: + CompressGCMarker() = delete; + inline ~CompressGCMarker() = default; + NO_COPY_SEMANTIC(CompressGCMarker); + NO_MOVE_SEMANTIC(CompressGCMarker); + + inline explicit CompressGCMarker(CompressCollector *compressCollector); + + inline void MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot); + +private: + inline void EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord, ObjectSlot slot); + inline void CopyObjectWithoutHeader(TaggedObject *object, uintptr_t address, size_t size); + + CompressCollector *collector_; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_COMPRESS_GC_MARKER_H diff --git a/ecmascript/mem/ecma_heap_manager-inl.h b/ecmascript/mem/ecma_heap_manager-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..2ccba6399b793205fa53c50cf007d7f8e2378456 --- /dev/null +++ b/ecmascript/mem/ecma_heap_manager-inl.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_MANAGER_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_MANAGER_INL_H + +#include "ecmascript/mem/ecma_heap_manager.h" + +#include + +#include "ecmascript/mem/allocator-inl.h" +#include "ecmascript/mem/heap-inl.h" +#include "ecmascript/mem/heap_roots.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_hclass.h" + +namespace panda::ecmascript { +TaggedObject *EcmaHeapManager::AllocateYoungGenerationOrLargeObject(JSHClass *hclass) +{ + size_t size = hclass->GetObjectSize(); + return AllocateYoungGenerationOrLargeObject(hclass, size); +} + +TaggedObject *EcmaHeapManager::AllocateYoungGenerationOrLargeObject(JSHClass *hclass, size_t size) +{ + if (size > MAX_REGULAR_HEAP_OBJECT_SIZE) { + return AllocateLargeObject(hclass, size); + } + // regular objects + auto object = reinterpret_cast(newSpaceAllocator_.Allocate(size)); + if (LIKELY(object != nullptr)) { + SetClass(object, hclass); + heap_->OnAllocateEvent(reinterpret_cast(object)); + return object; + } + + // hclass must nonmovable + if (!heap_->FillNewSpaceAndTryGC(&newSpaceAllocator_)) { + LOG_ECMA_MEM(FATAL) << "OOM : extend failed"; + UNREACHABLE(); + } + object = reinterpret_cast(newSpaceAllocator_.Allocate(size)); + if (UNLIKELY(object == nullptr)) { + heap_->CollectGarbage(TriggerGCType::SEMI_GC); + object = reinterpret_cast(newSpaceAllocator_.Allocate(size)); + if (UNLIKELY(object == nullptr)) { + heap_->CollectGarbage(TriggerGCType::OLD_GC); + object = reinterpret_cast(newSpaceAllocator_.Allocate(size)); + if (UNLIKELY(object == nullptr)) { + heap_->ThrowOutOfMemoryError(size); + UNREACHABLE(); + } + } + } + + SetClass(object, hclass); + heap_->OnAllocateEvent(reinterpret_cast(object)); + return object; +} + +TaggedObject *EcmaHeapManager::TryAllocateYoungGeneration(size_t size) +{ + if (size > MAX_REGULAR_HEAP_OBJECT_SIZE) { + return nullptr; + } + return reinterpret_cast(newSpaceAllocator_.Allocate(size)); +} + +TaggedObject *EcmaHeapManager::AllocateNonMovableOrLargeObject(JSHClass *hclass, size_t size) +{ + if (size > MAX_REGULAR_HEAP_OBJECT_SIZE) { + return AllocateLargeObject(hclass, size); + } + auto object = reinterpret_cast(nonMovableAllocator_.Allocate(size)); + if (UNLIKELY(object == nullptr)) { + if (heap_->CheckAndTriggerNonMovableGC()) { + object = reinterpret_cast(nonMovableAllocator_.Allocate(size)); + } + if (UNLIKELY(object == nullptr)) { + // hclass must be nonmovable + if (!heap_->FillNonMovableSpaceAndTryGC(&nonMovableAllocator_)) { + LOG_ECMA_MEM(FATAL) << "OOM : extend failed"; + UNREACHABLE(); + } + object = reinterpret_cast(nonMovableAllocator_.Allocate(size)); + if (UNLIKELY(object == nullptr)) { + heap_->ThrowOutOfMemoryError(size); + UNREACHABLE(); + } + } + } + SetClass(object, hclass); + heap_->OnAllocateEvent(reinterpret_cast(object)); + return object; +} + +uintptr_t EcmaHeapManager::AllocateSnapShotSpace(size_t size) +{ + uintptr_t object = snapshotSpaceAllocator_.Allocate(size); + if (UNLIKELY(object == 0)) { + if (!heap_->FillSnapShotSpace(&snapshotSpaceAllocator_)) { + LOG_ECMA_MEM(FATAL) << "alloc failed"; + UNREACHABLE(); + } + object = snapshotSpaceAllocator_.Allocate(size); + if (UNLIKELY(object == 0)) { + LOG_ECMA_MEM(FATAL) << "alloc failed"; + UNREACHABLE(); + } + } + return object; +} + +void EcmaHeapManager::SetClass(TaggedObject *header, JSHClass *hclass) +{ + header->SetClass(hclass); +} + +TaggedObject *EcmaHeapManager::AllocateNonMovableOrLargeObject(JSHClass *hclass) +{ + size_t size = hclass->GetObjectSize(); + return AllocateNonMovableOrLargeObject(hclass, size); +} + +TaggedObject *EcmaHeapManager::AllocateOldGenerationOrLargeObject(JSHClass *hclass, size_t size) +{ + ASSERT(size > 0); + if (size > MAX_REGULAR_HEAP_OBJECT_SIZE) { + return AllocateLargeObject(hclass, size); + } + auto object = reinterpret_cast(oldSpaceAllocator_.Allocate(size)); + if (UNLIKELY(object == nullptr)) { + if (heap_->CheckAndTriggerOldGC()) { + object = reinterpret_cast(oldSpaceAllocator_.Allocate(size)); + } + if (UNLIKELY(object == nullptr)) { + // hclass must nonmovable + if (!heap_->FillOldSpaceAndTryGC(&oldSpaceAllocator_)) { + LOG_ECMA_MEM(FATAL) << "OOM : extend failed"; + UNREACHABLE(); + } + object = reinterpret_cast(oldSpaceAllocator_.Allocate(size)); + if (UNLIKELY(object == nullptr)) { + heap_->ThrowOutOfMemoryError(size); + UNREACHABLE(); + } + } + } + SetClass(object, hclass); + heap_->OnAllocateEvent(reinterpret_cast(object)); + return object; +} + +TaggedObject *EcmaHeapManager::AllocateLargeObject(JSHClass *hclass, size_t size) +{ + ASSERT(size > MAX_REGULAR_HEAP_OBJECT_SIZE); + // large objects + heap_->CheckAndTriggerOldGC(); + auto *object = reinterpret_cast(heap_->GetLargeObjectSpace()->Allocate(size)); + if (UNLIKELY(object == nullptr)) { + heap_->CollectGarbage(TriggerGCType::LARGE_GC); + object = reinterpret_cast(heap_->GetLargeObjectSpace()->Allocate(size)); + if (UNLIKELY(object == nullptr)) { + heap_->ThrowOutOfMemoryError(size); + UNREACHABLE(); + } + } + SetClass(object, hclass); + heap_->OnAllocateEvent(reinterpret_cast(object)); + return object; +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_MANAGER_INL_H diff --git a/ecmascript/mem/ecma_heap_manager.cpp b/ecmascript/mem/ecma_heap_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..618676bbf9639be19848bfd5d8d2ce5740362749 --- /dev/null +++ b/ecmascript/mem/ecma_heap_manager.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/ecma_heap_manager-inl.h" +#include "ecmascript/mem/heap.h" + +namespace panda::ecmascript { +EcmaHeapManager::EcmaHeapManager(Heap *heap) + : heap_(heap), + newSpaceAllocator_(heap->GetNewSpace()), + nonMovableAllocator_(heap->GetNonMovableSpace()), + oldSpaceAllocator_(heap->GetOldSpace()) +{ + ASSERT(heap != nullptr); + heap->SetHeapManager(this); +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/ecma_heap_manager.h b/ecmascript/mem/ecma_heap_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..c55a07ca73726bd3c2428c6ab7a909992ec3d1d0 --- /dev/null +++ b/ecmascript/mem/ecma_heap_manager.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_ECMA_HEAP_MANAGER_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_ECMA_HEAP_MANAGER_H + +#include "ecmascript/mem/allocator-inl.h" +#include "ecmascript/js_hclass.h" + +namespace panda::ecmascript { +class Heap; + +class EcmaHeapManager { +public: + explicit EcmaHeapManager(Heap *heap); + ~EcmaHeapManager() = default; + + NO_COPY_SEMANTIC(EcmaHeapManager); + NO_MOVE_SEMANTIC(EcmaHeapManager); + + inline TaggedObject *AllocateYoungGenerationOrLargeObject(JSHClass *hclass); + inline TaggedObject *TryAllocateYoungGeneration(size_t size); + inline TaggedObject *AllocateYoungGenerationOrLargeObject(JSHClass *hclass, size_t size); + + inline TaggedObject *AllocateNonMovableOrLargeObject(JSHClass *hclass, size_t size); + inline TaggedObject *AllocateNonMovableOrLargeObject(JSHClass *hclass); + inline TaggedObject *AllocateLargeObject(JSHClass *hclass, size_t size); + inline TaggedObject *AllocateOldGenerationOrLargeObject(JSHClass *hclass, size_t size); + + inline uintptr_t AllocateSnapShotSpace(size_t size); + + inline void SetClass(TaggedObject *header, JSHClass *hclass); + + const Heap *GetHeap() const + { + return heap_; + } + + FreeListAllocator &GetOldSpaceAllocator() + { + return oldSpaceAllocator_; + } + + BumpPointerAllocator &GetNewSpaceAllocator() + { + return newSpaceAllocator_; + } + + FreeListAllocator &GetNonMovableSpaceAllocator() + { + return nonMovableAllocator_; + } + + const BumpPointerAllocator &GetSnapShotSpaceAllocator() const + { + return snapshotSpaceAllocator_; + } + +private: + Heap *heap_{nullptr}; + BumpPointerAllocator newSpaceAllocator_; + FreeListAllocator nonMovableAllocator_; + FreeListAllocator oldSpaceAllocator_; + BumpPointerAllocator snapshotSpaceAllocator_; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_ECMA_HEAP_MANAGER_H diff --git a/ecmascript/mem/ecma_list.h b/ecmascript/mem/ecma_list.h new file mode 100644 index 0000000000000000000000000000000000000000..d22aa473e360006d146c41f8e53c86fa64c1ef4a --- /dev/null +++ b/ecmascript/mem/ecma_list.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_ECMALIST_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_ECMALIST_H + +#include "ecmascript/mem/mem.h" + +namespace panda::ecmascript { +// Invoking std::list will cause cross invoking, which is time-consuming. +// Therefore, we implement ecma list inside the vm. + +template +class EcmaList { +public: + EcmaList() : first_(nullptr), last_(nullptr) {} + + explicit EcmaList(T *node) : first_(node), last_(node) + { + node->LinkPrev(nullptr); + node->LinkNext(nullptr); + } + ~EcmaList() = default; + NO_COPY_SEMANTIC(EcmaList); + NO_MOVE_SEMANTIC(EcmaList); + void AddNode(T *node) + { + ASSERT(node != nullptr); + if (LIKELY(first_ != nullptr)) { + T *lastNext = last_->GetNext(); + node->LinkNext(lastNext); + node->LinkPrev(last_); + last_->LinkNext(node); + if (lastNext) { + lastNext->LinkPrev(node); + } else { + last_ = node; + } + } else { + node->LinkPrev(nullptr); + node->LinkNext(nullptr); + first_ = last_ = node; + } + length_++; + } + + T *PopBack() + { + T *node = last_; + RemoveNode(last_); + return node; + } + + void RemoveNode(T *node) + { + ASSERT(HasNode(node)); + if (last_ == node) { + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + last_ = node->GetPrev(); + } + if (first_ == node) { + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + first_ = node->GetNext(); + } + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + T *next = node->GetNext(); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + T *prev = node->GetPrev(); + if (next != nullptr) { + next->LinkPrev(prev); + } + if (prev != nullptr) { + prev->LinkNext(next); + } + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + node->LinkPrev(nullptr); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + node->LinkNext(nullptr); + length_--; + } + + bool HasNode(T *node) + { + T *it = first_; + while (it != nullptr) { + if (it == node) { + return true; + } + it = it->GetNext(); + } + return false; + } + + T *GetFirst() const + { + return first_; + } + + T *GetLast() const + { + return last_; + } + + bool IsEmpty() const + { + return last_ == nullptr; + } + + void Clear() + { + first_ = last_ = nullptr; + length_ = 0; + } + + uint32_t GetLength() const + { + return length_; + } + +private: + T *first_; + T *last_; + uint32_t length_{0}; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_ECMALIST_H \ No newline at end of file diff --git a/ecmascript/mem/free_object_kind.cpp b/ecmascript/mem/free_object_kind.cpp new file mode 100644 index 0000000000000000000000000000000000000000..951871f25276b489301f68b343c81a5cb7d650c0 --- /dev/null +++ b/ecmascript/mem/free_object_kind.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/free_object_kind.h" + +#include "ecmascript/free_object.h" +#include "ecmascript/mem/free_object_list.h" + +namespace panda::ecmascript { +void FreeObjectKind::Free(uintptr_t begin, size_t size) +{ + auto freeObject = FreeObject::Cast(begin); + freeObject->SetNext(freeObject_); + freeObject_ = freeObject; + available_ += size; +} + +void FreeObjectKind::Rebuild() +{ + freeObject_ = nullptr; + available_ = 0; +} + +FreeObject *FreeObjectKind::SearchSmallFreeObject(size_t size) +{ + FreeObject *curFreeObject = nullptr; + if (freeObject_ != nullptr && freeObject_->Available() >= size) { + curFreeObject = freeObject_; + freeObject_ = freeObject_->GetNext(); + curFreeObject->SetNext(nullptr); + available_ -= curFreeObject->Available(); + } + return curFreeObject; +} + +FreeObject *FreeObjectKind::SearchLargeFreeObject(size_t size) +{ + FreeObject *prevFreeObject = freeObject_; + FreeObject *curFreeObject = freeObject_; + while (curFreeObject != nullptr) { + if (curFreeObject->Available() >= size) { + if (curFreeObject == freeObject_) { + freeObject_ = curFreeObject->GetNext(); + } else { + prevFreeObject->SetNext(curFreeObject->GetNext()); + } + curFreeObject->SetNext(nullptr); + available_ -= curFreeObject->Available(); + return curFreeObject; + } + prevFreeObject = curFreeObject; + curFreeObject = curFreeObject->GetNext(); + } + return nullptr; +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/free_object_kind.h b/ecmascript/mem/free_object_kind.h new file mode 100644 index 0000000000000000000000000000000000000000..f8bd144355c32ec866d07403488008fd06be181f --- /dev/null +++ b/ecmascript/mem/free_object_kind.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_FREE_OBJECT_KIND_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_FREE_OBJECT_KIND_H + +#include + +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +using KindType = int32_t; + +class FreeObject; + +class FreeObjectKind { +public: + FreeObjectKind(KindType type, uintptr_t begin, size_t size) : kindType_(type) + { + Free(begin, size); + } + ~FreeObjectKind() = default; + + inline bool Empty() const + { + return available_ == 0; + } + + inline size_t Available() const + { + return available_; + } + + void Free(uintptr_t begin, size_t size); + + void Rebuild(); + + FreeObject *SearchSmallFreeObject(size_t size); + FreeObject *SearchLargeFreeObject(size_t size); + + NO_COPY_SEMANTIC(FreeObjectKind); + NO_MOVE_SEMANTIC(FreeObjectKind); + + static constexpr KindType INVALID_KIND_TYPE = -1; + +private: + FreeObjectKind *next_ = nullptr; + FreeObjectKind *prev_ = nullptr; + KindType kindType_ = INVALID_KIND_TYPE; + size_t available_ = 0; + FreeObject *freeObject_ = nullptr; + + friend class FreeObjectList; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_FREE_OBJECT_LIST_H diff --git a/ecmascript/mem/free_object_list-inl.h b/ecmascript/mem/free_object_list-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..cc48091b31c769b2591dc7097c7faeb553884590 --- /dev/null +++ b/ecmascript/mem/free_object_list-inl.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_FREE_OBJECT_LIST_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_FREE_OBJECT_LIST_INL_H + +#include "ecmascript/mem/free_object_list.h" + +#include + +namespace panda::ecmascript { +KindType FreeObjectList::SelectKindType(size_t size) const +{ + if (size < SMALL_KIND_MAX_SIZE) { + if (UNLIKELY(size < MIN_SIZE)) { + return FreeObjectKind::INVALID_KIND_TYPE; + } + return (size >> INTERVAL_OFFSET) - smallKindOffsetIndex; + } + if (size < LARGE_KIND_MAX_SIZE) { + return MAX_BIT_OF_SIZET - __builtin_clzl(size) + LOG2_OFFSET; + } + if (size >= HUGE_KIND_MAX_SIZE) { + return NUMBER_OF_LAST_HUGE; + } + + return NUMBER_OF_LAST_LARGE; +} + +void FreeObjectList::SetNoneEmptyBit(KindType type) +{ + noneEmptyKindBitMap_ |= 1ULL << static_cast(type); +} + +void FreeObjectList::ClearNoneEmptyBit(KindType type) +{ + noneEmptyKindBitMap_ &= ~(1ULL << static_cast(type)); +} + +inline size_t FreeObjectList::CalcNextNoneEmptyIndex(KindType start) +{ + return __builtin_ffsll(noneEmptyKindBitMap_ >> static_cast(start)) + start - 1; +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_FREE_OBJECT_LIST_INL_H diff --git a/ecmascript/mem/free_object_list.cpp b/ecmascript/mem/free_object_list.cpp new file mode 100644 index 0000000000000000000000000000000000000000..43e220f78e4fa0f7dc54117e286f06528e31be06 --- /dev/null +++ b/ecmascript/mem/free_object_list.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/free_object_list.h" + +#include "ecmascript/free_object.h" +#include "ecmascript/mem/free_object_list-inl.h" +#include "ecmascript/mem/mem.h" + +namespace panda::ecmascript { +FreeObjectList::FreeObjectList() +{ + kinds_ = Span(new FreeObjectKind *[NUMBER_OF_KINDS](), NUMBER_OF_KINDS); + noneEmptyKindBitMap_ = 0; +} + +FreeObjectList::~FreeObjectList() +{ + for (auto it : kinds_) { + delete it; + } + delete[] kinds_.data(); + noneEmptyKindBitMap_ = 0; +} + +FreeObject *FreeObjectList::Allocator(size_t size) +{ + if (noneEmptyKindBitMap_ == 0) { + return nullptr; + } + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + // find from suitable + KindType type = SelectKindType(size); + if (type == FreeObjectKind::INVALID_KIND_TYPE) { + return nullptr; + } + + KindType lastType = type - 1; + for (type = CalcNextNoneEmptyIndex(type); type > lastType && type < NUMBER_OF_KINDS; + type = CalcNextNoneEmptyIndex(type + 1)) { + lastType = type; + FreeObjectKind *top = kinds_[type]; + if (top == nullptr || top->Available() < size) { + continue; + } + FreeObject *current = nullptr; + if (type <= SMALL_KIND_MAX_INDEX) { + current = top->SearchSmallFreeObject(size); + } else { + current = top->SearchLargeFreeObject(size); + } + if (top->Empty()) { + RemoveKind(top); + } + if (current != nullptr) { + size_t currentSize = current->Available(); + available_ -= currentSize; + if (currentSize >= size) { + return current; + } + } + } + return nullptr; +} + +void FreeObjectList::Free(uintptr_t start, size_t size) +{ + if (start == 0 || size == 0) { + return; + } + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + KindType type = SelectKindType(size); + if (type == FreeObjectKind::INVALID_KIND_TYPE) { + return; + } + + auto kind = kinds_[type]; + if (kind == nullptr) { + kind = new FreeObjectKind(type, start, size); + if (!AddKind(kind)) { + delete kind; + return; + } + } else { + kind->Free(start, size); + } + available_ += size; +} + +void FreeObjectList::Rebuild() +{ + for (auto kind : kinds_) { + if (kind != nullptr) { + kind->Rebuild(); + RemoveKind(kind); + } + } + available_ = 0; + noneEmptyKindBitMap_ = 0; +} + +size_t FreeObjectList::GetFreeObjectSize() const +{ + return available_; +} + +bool FreeObjectList::AddKind(FreeObjectKind *kind) +{ + if (kind == nullptr || kind->Empty()) { + return false; + } + KindType type = kind->kindType_; + FreeObjectKind *top = kinds_[type]; + if (kind == top) { + return true; + } + if (top != nullptr) { + top->prev_ = kind; + } + kind->next_ = top; + kinds_[type] = kind; + SetNoneEmptyBit(type); + return true; +} + +void FreeObjectList::RemoveKind(FreeObjectKind *kind) +{ + if (kind == nullptr) { + return; + } + KindType type = kind->kindType_; + FreeObjectKind *top = kinds_[type]; + if (top == kind) { + kinds_[type] = top->next_; + } + if (kind->prev_ != nullptr) { + kind->prev_->next_ = kind->next_; + } + if (kind->next_ != nullptr) { + kind->next_->prev_ = kind->prev_; + } + kind->prev_ = nullptr; + kind->next_ = nullptr; + if (kinds_[type] == nullptr) { + ClearNoneEmptyBit(type); + } + delete kind; +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/free_object_list.h b/ecmascript/mem/free_object_list.h new file mode 100644 index 0000000000000000000000000000000000000000..5726cfd0e2b5f24929dba12bc68cb53a1c6fa106 --- /dev/null +++ b/ecmascript/mem/free_object_list.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_FREE_OBJECT_LIST_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_FREE_OBJECT_LIST_H + +#include + +#include "ecmascript/mem/free_object_kind.h" +#include "utils/span.h" + +namespace panda::ecmascript { +class FreeObjectList { +public: + FreeObjectList(); + ~FreeObjectList(); + + FreeObject *Allocator(size_t size); + + void Free(uintptr_t start, size_t size); + + void Rebuild(); + + NO_COPY_SEMANTIC(FreeObjectList); + NO_MOVE_SEMANTIC(FreeObjectList); + + size_t GetFreeObjectSize() const; + +private: + static constexpr int NUMBER_OF_KINDS = 39; + static constexpr size_t MIN_SIZE = 16; + static constexpr size_t SMALL_KIND_MAX_SIZE = 256; + static constexpr size_t LARGE_KIND_MAX_SIZE = 65536; + static constexpr size_t HUGE_KIND_MAX_SIZE = 255 * 1024; + static constexpr int SMALL_KIND_MAX_INDEX = 29; + static constexpr int NUMBER_OF_LAST_LARGE = NUMBER_OF_KINDS - 2; + static constexpr int NUMBER_OF_LAST_HUGE = NUMBER_OF_KINDS - 1; + static constexpr size_t INTERVAL_OFFSET = 3; + static constexpr size_t LOG2_OFFSET = 21; + static constexpr size_t MAX_BIT_OF_SIZET = sizeof(size_t) << INTERVAL_OFFSET; + const int smallKindOffsetIndex = 2; + + inline KindType SelectKindType(size_t size) const; + + inline void SetNoneEmptyBit(KindType type); + inline void ClearNoneEmptyBit(KindType type); + inline size_t CalcNextNoneEmptyIndex(KindType start); + + bool AddKind(FreeObjectKind *kind); + void RemoveKind(FreeObjectKind *kind); + + size_t available_ = 0; + uint64_t noneEmptyKindBitMap_; + Span kinds_ {}; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_FREE_OBJECT_LIST_H diff --git a/ecmascript/mem/gc_stats.cpp b/ecmascript/mem/gc_stats.cpp new file mode 100644 index 0000000000000000000000000000000000000000..531d769c24b3a1b7edbe51c452b049481331646b --- /dev/null +++ b/ecmascript/mem/gc_stats.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/gc_stats.h" + +#include "ecmascript/mem/mem.h" + +namespace panda::ecmascript { +void GCStats::PrintStatisticResult() +{ + LOG(DEBUG, RUNTIME) << "GCStats statistic: "; + if (semiGCCount_ != 0) { + LOG(DEBUG, RUNTIME) << " SemiCollector statistic: total semi gc count " << semiGCCount_; + LOG(DEBUG, RUNTIME) << " MIN pause time: " << PrintTimeMilliseconds(semiGCMinPause_) << "ms" + << " MAX pause time: " << PrintTimeMilliseconds(semiGCMAXPause_) << "ms" + << " total pause time: " << PrintTimeMilliseconds(semiGCTotalPause_) << "ms" + << " average pause time: " << PrintTimeMilliseconds(semiGCTotalPause_ / semiGCCount_) + << "ms" + << " tatal alive size: " << sizeToMB(semiTotalAliveSize_) << "MB" + << " average alive size: " << sizeToMB(semiTotalAliveSize_ / semiGCCount_) << "MB" + << " tatal commit size: " << sizeToMB(semiTotalCommitSize_) << "MB" + << " average commit size: " << sizeToMB(semiTotalCommitSize_ / semiGCCount_) << "MB" + << " semi aliveRate: " << double(semiTotalAliveSize_) / semiTotalCommitSize_ + << " total promote size: " << sizeToMB(semiTotalPromoteSize_) << "MB" + << " average promote size: " << sizeToMB(semiTotalPromoteSize_ / semiGCCount_) << "MB"; + } + + if (oldGCCount_ != 0) { + LOG(DEBUG, RUNTIME) << " oldCollector statistic: total old gc count " << oldGCCount_; + LOG(DEBUG, RUNTIME) << " MIN pause time: " << PrintTimeMilliseconds(oldGCMinPause_) << "ms" + << " MAX pause time: " << PrintTimeMilliseconds(oldGCMAXPause_) << "ms" + << " total pause time: " << PrintTimeMilliseconds(oldGCTotalPause_) << "ms" + << " average pause time: " << PrintTimeMilliseconds(oldGCTotalPause_ / oldGCCount_) << "ms" + << " total free size: " << sizeToMB(oldTotalFreeSize_) << "MB" + << " average free size: " << sizeToMB(oldTotalFreeSize_ / oldGCCount_) << "MB" + << " old space total commit size: " << sizeToMB(oldSpaceTotalCommitSize_) << "MB" + << " old space average commit size: " << sizeToMB(oldSpaceTotalCommitSize_ / oldGCCount_) + << "MB" + << " non move space total commit size: " << sizeToMB(oldNonMoveTotalCommitSize_) << "MB" + << " non move space average commit size: " + << sizeToMB(oldNonMoveTotalCommitSize_ / oldGCCount_) << "MB" + << " old free rate: " + << float(oldTotalFreeSize_) / (oldSpaceTotalCommitSize_ + oldNonMoveTotalCommitSize_); + } + + if (compressGCCount_ != 0) { + LOG(DEBUG, RUNTIME) << " compressCollector statistic: total compress gc count " << compressGCCount_; + LOG(DEBUG, RUNTIME) + << " MIN pause time: " << PrintTimeMilliseconds(compressGCMinPause_) << "ms" + << " MAX pause time: " << PrintTimeMilliseconds(compressGCMaxPause_) << "ms" + << " total pause time: " << PrintTimeMilliseconds(compressGCTotalPause_) << "ms" + << " average pause time: " << PrintTimeMilliseconds(compressGCTotalPause_ / compressGCCount_) << "ms" + << " young and old total alive size: " << sizeToMB(compressYoungAndOldAliveSize_) << "MB" + << " young and old average alive size: " << sizeToMB(compressYoungAndOldAliveSize_ / compressGCCount_) + << "MB" + << " young total commit size: " << sizeToMB(compressYoungCommitSize_) << "MB" + << " old total commit size: " << sizeToMB(compressOldCommitSize_) << "MB" + << " young and old average commit size: " + << sizeToMB((compressYoungCommitSize_ + compressOldCommitSize_) / compressGCCount_) << "MB" + << " young and old free rate: " + << 1 - float(compressYoungAndOldAliveSize_) / (compressYoungCommitSize_ + compressOldCommitSize_) + << " non move total free size: " << sizeToMB(compressNonMoveTotalFreeSize_) << "MB" + << " non move total commit size: " << sizeToMB(compressNonMoveTotalCommitSize_) << "MB" + << " non move free rate: " << float(compressNonMoveTotalFreeSize_) / compressNonMoveTotalCommitSize_; + } +} + +void GCStats::StatisticSemiCollector(Duration time, size_t aliveSize, size_t promoteSize, size_t commitSize) +{ + auto timeToMillion = TimeToMicroseconds(time); + if (semiGCCount_ == 0) { + semiGCMinPause_ = timeToMillion; + semiGCMAXPause_ = timeToMillion; + } else { + semiGCMinPause_ = std::min(semiGCMinPause_, timeToMillion); + semiGCMAXPause_ = std::max(semiGCMAXPause_, timeToMillion); + } + semiGCTotalPause_ += timeToMillion; + semiTotalAliveSize_ += aliveSize; + semiTotalCommitSize_ += commitSize; + semiTotalPromoteSize_ += promoteSize; + semiGCCount_++; +} + +void GCStats::StatisticOldCollector(Duration time, size_t freeSize, size_t oldSpaceCommitSize, + size_t nonMoveSpaceCommitSize) +{ + auto timeToMillion = TimeToMicroseconds(time); + if (oldGCCount_ == 0) { + oldGCMinPause_ = timeToMillion; + oldGCMAXPause_ = timeToMillion; + } else { + oldGCMinPause_ = std::min(oldGCMinPause_, timeToMillion); + oldGCMAXPause_ = std::max(oldGCMAXPause_, timeToMillion); + } + oldGCTotalPause_ += timeToMillion; + oldTotalFreeSize_ += freeSize; + oldSpaceTotalCommitSize_ += oldSpaceCommitSize; + oldNonMoveTotalCommitSize_ += nonMoveSpaceCommitSize; + oldGCCount_++; +} + +void GCStats::StatisticCompressCollector(Duration time, size_t youngAndOldAliveSize, size_t youngCommitSize, + size_t oldCommitSize, size_t nonMoveSpaceFreeSize, + size_t nonMoveSpaceCommitSize) +{ + auto timeToMillion = TimeToMicroseconds(time); + if (compressGCCount_ == 0) { + compressGCMinPause_ = timeToMillion; + compressGCMaxPause_ = timeToMillion; + } else { + compressGCMinPause_ = std::min(compressGCMinPause_, timeToMillion); + compressGCMaxPause_ = std::max(compressGCMaxPause_, timeToMillion); + } + compressGCTotalPause_ += timeToMillion; + compressYoungAndOldAliveSize_ += youngAndOldAliveSize; + compressYoungCommitSize_ += youngCommitSize; + compressOldCommitSize_ += oldCommitSize; + compressNonMoveTotalFreeSize_ += nonMoveSpaceFreeSize; + compressNonMoveTotalCommitSize_ += nonMoveSpaceCommitSize; + compressGCCount_++; +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/mem/gc_stats.h b/ecmascript/mem/gc_stats.h new file mode 100644 index 0000000000000000000000000000000000000000..3cf36b3afceac8eedb81f8e12d657a043f7a3bc4 --- /dev/null +++ b/ecmascript/mem/gc_stats.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_GC_STATS_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_GC_STATS_H + +#include "time.h" +#include "chrono" +#include "libpandabase/utils/logger.h" + +namespace panda::ecmascript { +class GCStats { + using Duration = std::chrono::duration; + +public: + GCStats() = default; + ~GCStats() = default; + + void PrintStatisticResult(); + + void StatisticSemiCollector(Duration time, size_t aliveSize, size_t promoteSize, size_t commitSize); + void StatisticOldCollector(Duration time, size_t freeSize, size_t oldSpaceCommitSize, + size_t nonMoveSpaceCommitSize); + void StatisticCompressCollector(Duration time, size_t youngAndOldAliveSize, size_t youngCommitSize, + size_t oldCommitSize, size_t nonMoveSpaceFreeSize, size_t nonMoveSpaceCommitSize); + +private: + size_t TimeToMicroseconds(Duration time) + { + return std::chrono::duration_cast(time).count(); + } + + float PrintTimeMilliseconds(uint64_t time) + { + return (float)time / MILLION_TIME; + } + + float sizeToMB(size_t size) + { + return (float)size / MB; + } + + size_t semiGCCount_ = 0; + size_t semiGCMinPause_ = 0; + size_t semiGCMAXPause_ = 0; + size_t semiGCTotalPause_ = 0; + size_t semiTotalAliveSize_ = 0; + size_t semiTotalCommitSize_ = 0; + size_t semiTotalPromoteSize_ = 0; + + size_t oldGCCount_ = 0; + size_t oldGCMinPause_ = 0; + size_t oldGCMAXPause_ = 0; + size_t oldGCTotalPause_ = 0; + size_t oldTotalFreeSize_ = 0; + size_t oldSpaceTotalCommitSize_ = 0; + size_t oldNonMoveTotalCommitSize_ = 0; + + size_t compressGCCount_ = 0; + size_t compressGCMinPause_ = 0; + size_t compressGCMaxPause_ = 0; + size_t compressGCTotalPause_ = 0; + size_t compressYoungAndOldAliveSize_ = 0; + size_t compressYoungCommitSize_ = 0; + size_t compressOldCommitSize_ = 0; + size_t compressNonMoveTotalFreeSize_ = 0; + size_t compressNonMoveTotalCommitSize_ = 0; + + static constexpr uint32_t MILLION_TIME = 1000; + static constexpr uint32_t MB = 1 * 1024 * 1024; + + NO_COPY_SEMANTIC(GCStats); + NO_MOVE_SEMANTIC(GCStats); +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_GC_STATS_H \ No newline at end of file diff --git a/ecmascript/mem/gc_write_barrier.h b/ecmascript/mem/gc_write_barrier.h new file mode 100644 index 0000000000000000000000000000000000000000..265ec6cf360685d518743af283039e1b86592b1f --- /dev/null +++ b/ecmascript/mem/gc_write_barrier.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_GC_WRITE_BARRIER_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_GC_WRITE_BARRIER_H + +#include "ecmascript/mem/mem.h" +#include "ecmascript/mem/region.h" + +namespace panda::ecmascript { +static inline void MarkingBarrier(void *obj, size_t offset, TaggedObject *value) +{ + ASSERT(ToUintPtr(value) != 0xa); + Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast(obj)); + Region *valueRegion = Region::ObjectAddressToRange(value); + if (!objectRegion->InYoungGeneration() && valueRegion->InYoungGeneration()) { + [[maybe_unused]] uintptr_t slotAddr = ToUintPtr(obj) + offset; + objectRegion->InsertOldToNewRememberedSet(slotAddr); + } +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_GC_WRITE_BARRIER_H \ No newline at end of file diff --git a/ecmascript/mem/heap-inl.h b/ecmascript/mem/heap-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..9455f765d648d9a03efe3499219d6cba9b3e96d6 --- /dev/null +++ b/ecmascript/mem/heap-inl.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_INL_H + +#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/mem_controller.h" +#include "ecmascript/mem/space.h" +#include "ecmascript/hprof/heap_tracker.h" +#include "ecmascript/mem/remembered_set.h" + +namespace panda::ecmascript { +template +void Heap::EnumerateOldSpaceRegions(const Callback &cb, Region *region) const +{ + oldSpace_->EnumerateRegions(cb, region); + nonMovableSpace_->EnumerateRegions(cb); + largeObjectSpace_->EnumerateRegions(cb); +} + +template +void Heap::EnumerateSnapShotSpaceRegions(const Callback &cb) const +{ + snapshotSpace_->EnumerateRegions(cb); +} + +template +void Heap::EnumerateNewSpaceRegions(const Callback &cb) const +{ + toSpace_->EnumerateRegions(cb); +} + +template +void Heap::EnumerateRegions(const Callback &cb) const +{ + toSpace_->EnumerateRegions(cb); + oldSpace_->EnumerateRegions(cb); + snapshotSpace_->EnumerateRegions(cb); + nonMovableSpace_->EnumerateRegions(cb); + largeObjectSpace_->EnumerateRegions(cb); +} + +template +void Heap::IteratorOverObjects(const Callback &cb) const +{ + toSpace_->IterateOverObjects(cb); + oldSpace_->IterateOverObjects(cb); + nonMovableSpace_->IterateOverObjects(cb); +} + +bool Heap::FillNewSpaceAndTryGC(BumpPointerAllocator *spaceAllocator, bool allowGc) +{ + if (toSpace_->Expand(spaceAllocator->GetTop())) { + spaceAllocator->Reset(toSpace_); + return true; + } + if (!allowGc) { + return false; + } + if (!CheckAndTriggerCompressGC()) { + CollectGarbage(TriggerGCType::SEMI_GC); + } + return true; +} + +bool Heap::FillOldSpaceAndTryGC(FreeListAllocator *spaceAllocator, bool allowGc) +{ + if (oldSpace_->Expand()) { + spaceAllocator->AddFree(oldSpace_->GetCurrentRegion()); + return true; + } + if (allowGc) { + CollectGarbage(TriggerGCType::OLD_GC); + return true; + } + return false; +} + +bool Heap::FillNonMovableSpaceAndTryGC(FreeListAllocator *spaceAllocator, bool allowGc) +{ + if (nonMovableSpace_->Expand()) { + spaceAllocator->AddFree(nonMovableSpace_->GetCurrentRegion()); + return true; + } + if (allowGc) { + CollectGarbage(TriggerGCType::NON_MOVE_GC); + return true; + } + return false; +} + +bool Heap::FillSnapShotSpace(BumpPointerAllocator *spaceAllocator) +{ + bool result = snapshotSpace_->Expand(spaceAllocator->GetTop()); + if (result) { + spaceAllocator->Reset(snapshotSpace_); + } + return result; +} + +void Heap::OnAllocateEvent(uintptr_t address) +{ + if (tracker_ != nullptr) { + tracker_->AllocationEvent(address); + } +} + +void Heap::OnMoveEvent(uintptr_t address, uintptr_t forwardAddress) +{ + if (tracker_ != nullptr) { + tracker_->MoveEvent(address, forwardAddress); + } +} + +void Heap::SetNewSpaceAgeMark(uintptr_t mark) +{ + ASSERT(toSpace_ != nullptr); + toSpace_->SetAgeMark(mark); +} + +void Heap::SetNewSpaceMaximumCapacity(size_t maximumCapacity) +{ + ASSERT(toSpace_ != nullptr); + SetMaximumCapacity(toSpace_, maximumCapacity); +} + +void Heap::InitializeFromSpace() +{ + ASSERT(fromSpace_ != nullptr); + fromSpace_->SetUp(); +} + +void Heap::SwapSpace() +{ + ASSERT(toSpace_ != nullptr); + ASSERT(fromSpace_ != nullptr); + toSpace_->Swap(fromSpace_); +} + +void Heap::ReclaimFromSpaceRegions() +{ + ASSERT(fromSpace_ != nullptr); + fromSpace_->ReclaimRegions(); +} + +void Heap::SetFromSpaceMaximumCapacity(size_t maximumCapacity) +{ + ASSERT(fromSpace_ != nullptr); + SetMaximumCapacity(fromSpace_, maximumCapacity); +} + +void Heap::ResetAppStartup() +{ + ASSERT(memController_ != nullptr); + memController_->ResetAppStartup(); +} + +void Heap::SetMaximumCapacity(SemiSpace *space, size_t maximumCapacity) +{ + space->SetMaximumCapacity(maximumCapacity); +} + +void Heap::ClearSlotsRange(Region *current, uintptr_t freeStart, uintptr_t freeEnd) +{ + auto set = current->GetOldToNewRememberedSet(); + if (set != nullptr) { + set->ClearRange(freeStart, freeEnd); + } + set = current->GetCrossRegionRememberedSet(); + if (set != nullptr) { + set->ClearRange(freeStart, freeEnd); + } +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_INL_H diff --git a/ecmascript/mem/heap.cpp b/ecmascript/mem/heap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5dc81ba6c59416584ed588e689a0b5a87aa0667b --- /dev/null +++ b/ecmascript/mem/heap.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/heap.h" + +#include + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/mem/assert_scope-inl.h" +#include "ecmascript/mem/compress_collector.h" +#include "ecmascript/mem/ecma_heap_manager.h" +#include "ecmascript/mem/mark_stack.h" +#include "ecmascript/mem/mem_controller.h" +#include "ecmascript/mem/old_space_collector.h" +#include "ecmascript/mem/semi_space_collector.h" +#include "ecmascript/mem/semi_space_worker.h" +#include "ecmascript/mem/verification.h" + +namespace panda::ecmascript { +Heap::Heap(EcmaVM *ecmaVm) : ecmaVm_(ecmaVm), regionFactory_(ecmaVm->GetRegionFactory()) {} + +void Heap::SetUp() +{ + memController_ = CreateMemController("no-gc-for-start-up"); + if (GetMemController()->IsInAppStartup()) { + toSpace_ = new SemiSpace(this, DEFAULT_SEMI_SPACE_SIZE, MAX_SEMI_SPACE_SIZE_STARTUP); + fromSpace_ = new SemiSpace(this, DEFAULT_SEMI_SPACE_SIZE, MAX_SEMI_SPACE_SIZE_STARTUP); + } else { + toSpace_ = new SemiSpace(this); + fromSpace_ = new SemiSpace(this); + } + toSpace_->SetUp(); + // not set up from space + oldSpace_ = new OldSpace(this); + compressSpace_ = new OldSpace(this); + oldSpace_->SetUp(); + nonMovableSpace_ = new NonMovableSpace(this); + nonMovableSpace_->SetUp(); + snapshotSpace_ = new SnapShotSpace(this); + largeObjectSpace_ = new LargeObjectSpace(this); + markStack_ = new MarkStack(this); + weakProcessQueue_ = new ProcessQueue(this); + bool paralledGc = ecmaVm_->GetOptions().IsEnableParalledYoungGc(); + if (paralledGc) { + int numOfCpuCore = get_nprocs(); + int numThread = std::min(numOfCpuCore, THREAD_NUM_FOR_YOUNG_GC); + pool_ = new ThreadPool(numThread); + semiSpaceCollector_ = new SemiSpaceCollector(this, true); + compressCollector_ = new CompressCollector(this, true); + } else { + pool_ = new ThreadPool(1); + semiSpaceCollector_ = new SemiSpaceCollector(this, false); + compressCollector_ = new CompressCollector(this, false); + } + oldSpaceCollector_ = new OldSpaceCollector(this); +} + +void Heap::FlipNewSpace() +{ + SemiSpace *newSpace = fromSpace_; + fromSpace_ = toSpace_; + toSpace_ = newSpace; +} + +void Heap::FlipCompressSpace() +{ + OldSpace *oldSpace = compressSpace_; + compressSpace_ = oldSpace_; + oldSpace_ = oldSpace; +} +void Heap::TearDown() +{ + pool_->WaitTaskFinish(); + toSpace_->TearDown(); + delete toSpace_; + toSpace_ = nullptr; + fromSpace_->TearDown(); + delete fromSpace_; + fromSpace_ = nullptr; + + oldSpace_->TearDown(); + delete oldSpace_; + oldSpace_ = nullptr; + compressSpace_->TearDown(); + delete compressSpace_; + compressSpace_ = nullptr; + nonMovableSpace_->TearDown(); + delete nonMovableSpace_; + nonMovableSpace_ = nullptr; + snapshotSpace_->TearDown(); + delete snapshotSpace_; + snapshotSpace_ = nullptr; + markStack_->TearDown(); + delete markStack_; + markStack_ = nullptr; + largeObjectSpace_->TearDown(); + delete largeObjectSpace_; + largeObjectSpace_ = nullptr; + + weakProcessQueue_->TearDown(); + delete weakProcessQueue_; + weakProcessQueue_ = nullptr; + delete semiSpaceCollector_; + semiSpaceCollector_ = nullptr; + delete oldSpaceCollector_; + oldSpaceCollector_ = nullptr; + delete compressCollector_; + compressCollector_ = nullptr; + regionFactory_ = nullptr; + delete memController_; + memController_ = nullptr; + delete pool_; + pool_ = nullptr; +} + +void Heap::CollectGarbage(TriggerGCType gcType) +{ + CHECK_NO_GC + // pre gc heap verify + { + if (ecmaVm_->GetOptions().IsPreGcHeapVerifyEnabled()) { + auto failCount = Verification(this).VerifyAll(); + if (failCount > 0) { + LOG(FATAL, GC) << "Before gc heap corrupted and " << failCount << " corruptions"; + } + } + } + switch (gcType) { + case TriggerGCType::SEMI_GC: + if (GetMemController()->IsInAppStartup()) { + semiSpaceCollector_->RunPhases(); + SetFromSpaceMaximumCapacity(SEMI_SPACE_SIZE_4M); + SetNewSpaceMaximumCapacity(SEMI_SPACE_SIZE_4M); + ResetAppStartup(); + } else { + semiSpaceCollector_->RunPhases(); + } + break; + case TriggerGCType::OLD_GC: + if (oldSpace_->GetHeapObjectSize() < OLD_SPACE_LIMIT_BEGIN) { + oldSpaceCollector_->RunPhases(); + } else { + compressCollector_->RunPhases(); + } + RecomputeLimits(); + break; + case TriggerGCType::NON_MOVE_GC: + case TriggerGCType::LARGE_GC: + oldSpaceCollector_->RunPhases(); + RecomputeLimits(); + break; + case TriggerGCType::COMPRESS_FULL_GC: + compressCollector_->RunPhases(); + RecomputeLimits(); + break; + default: + UNREACHABLE(); + break; + } + + // post gc heap verify + { + if (ecmaVm_->GetOptions().IsPreGcHeapVerifyEnabled()) { + auto failCount = Verification(this).VerifyAll(); + if (failCount > 0) { + LOG(FATAL, GC) << "After gc heap corrupted and " << failCount << " corruptions"; + } + } + } +} + +void Heap::ThrowOutOfMemoryError(size_t size) +{ + LOG_ECMA_MEM(FATAL) << "OOM when trying to allocate " << size << " bytes"; +} + +size_t Heap::VerifyHeapObjects() const +{ + size_t failCount = 0; + { + VerifyObjectVisitor verifier(this, &failCount); + oldSpace_->IterateOverObjects(verifier); + } + + { + VerifyObjectVisitor verifier(this, &failCount); + nonMovableSpace_->IterateOverObjects(verifier); + } + + { + VerifyObjectVisitor verifier(this, &failCount); + largeObjectSpace_->IterateOverObjects(verifier); + } + + return failCount; +} + +void Heap::RecomputeLimits() +{ + size_t oldSpaceSize = oldSpace_->GetHeapObjectSize(); + size_t newSpaceCapacity = toSpace_->GetMaximumCapacity(); + + constexpr double GrowingFactor = 1.1; + auto newOldSpaceLimit = memController_->CalculateAllocLimit(oldSpaceSize, DEFAULT_OLD_SPACE_SIZE, + MAX_OLD_SPACE_SIZE, newSpaceCapacity, GrowingFactor); + oldSpaceAllocLimit_ = newOldSpaceLimit; +} + +bool Heap::CheckAndTriggerOldGC() +{ + if (oldSpace_->GetHeapObjectSize() <= oldSpaceAllocLimit_) { + return false; + } + CollectGarbage(TriggerGCType::OLD_GC); + return true; +} + +bool Heap::CheckAndTriggerCompressGC() +{ + if (oldSpace_->GetHeapObjectSize() <= oldSpaceAllocLimit_) { + return false; + } + CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + return true; +} + +bool Heap::CheckAndTriggerNonMovableGC() +{ + if (nonMovableSpace_->GetHeapObjectSize() <= DEFAULT_NON_MOVABLE_SPACE_LIMIT) { + return false; + } + CollectGarbage(TriggerGCType::NON_MOVE_GC); + return true; +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/heap.h b/ecmascript/mem/heap.h new file mode 100644 index 0000000000000000000000000000000000000000..631e22d5bebdb93420bffac67c736f7228366e1f --- /dev/null +++ b/ecmascript/mem/heap.h @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_H + +#include "ecmascript/thread/thread_pool.h" +#include "ecmascript/mem/mark_stack.h" +#include "ecmascript/mem/space.h" + +namespace panda::ecmascript { +class EcmaVM; +class EcmaHeapManager; +class SemiSpaceCollector; +class OldSpaceCollector; +class CompressCollector; +class BumpPointerAllocator; +class FreeListAllocator; +class RegionFactory; +class HeapTracker; +class MemController; + +class Heap { +public: + explicit Heap(EcmaVM *ecmaVm); + ~Heap() = default; + NO_COPY_SEMANTIC(Heap); + NO_MOVE_SEMANTIC(Heap); + void SetUp(); + void TearDown(); + + const SemiSpace *GetNewSpace() const + { + return toSpace_; + } + + inline void SetNewSpaceAgeMark(uintptr_t mark); + + inline void SetNewSpaceMaximumCapacity(size_t maximumCapacity); + + const SemiSpace *GetFromSpace() const + { + return fromSpace_; + } + + const OldSpace *GetCompressSpace() const + { + return compressSpace_; + } + + inline void InitializeFromSpace(); + + inline void SwapSpace(); + + inline void ReclaimFromSpaceRegions(); + + inline void SetFromSpaceMaximumCapacity(size_t maximumCapacity); + + void SetFromSpace(SemiSpace *space) + { + fromSpace_ = space; + } + + const OldSpace *GetOldSpace() const + { + return oldSpace_; + } + + const NonMovableSpace *GetNonMovableSpace() const + { + return nonMovableSpace_; + } + + LargeObjectSpace *GetLargeObjectSpace() const + { + return largeObjectSpace_; + } + + MarkStack *GetMarkStack() const + { + return markStack_; + } + + ProcessQueue *GetProcessQueue() const + { + return weakProcessQueue_; + } + + SemiSpaceCollector *GetSemiSpaceCollector() const + { + return semiSpaceCollector_; + } + + OldSpaceCollector *GetOldSpaceCollector() const + { + return oldSpaceCollector_; + } + + CompressCollector *GetCompressCollector() const + { + return compressCollector_; + } + + EcmaVM *GetEcmaVM() const + { + return ecmaVm_; + } + + ThreadPool *GetThreadPool() const + { + return pool_; + } + + void FlipNewSpace(); + + void FlipCompressSpace(); + + template + void EnumerateOldSpaceRegions(const Callback &cb, Region *region = nullptr) const; + + template + void EnumerateNewSpaceRegions(const Callback &cb) const; + + template + void EnumerateSnapShotSpaceRegions(const Callback &cb) const; + + template + void EnumerateRegions(const Callback &cb) const; + + template + void IteratorOverObjects(const Callback &cb) const; + + void CollectGarbage(TriggerGCType gcType); + + inline bool FillNewSpaceAndTryGC(BumpPointerAllocator *spaceAllocator, bool allowGc = true); + inline bool FillOldSpaceAndTryGC(FreeListAllocator *spaceAllocator, bool allowGc = true); + inline bool FillNonMovableSpaceAndTryGC(FreeListAllocator *spaceAllocator, bool allowGc = true); + inline bool FillSnapShotSpace(BumpPointerAllocator *spaceAllocator); + + void ThrowOutOfMemoryError(size_t size); + + void SetHeapManager(EcmaHeapManager *heapManager) + { + heapManager_ = heapManager; + } + + EcmaHeapManager *GetHeapManager() const + { + return heapManager_; + } + + void StartHeapTracking(HeapTracker *tracker) + { + tracker_ = tracker; + } + + void StopHeapTracking() + { + tracker_ = nullptr; + } + + inline void OnAllocateEvent(uintptr_t address); + inline void OnMoveEvent(uintptr_t address, uintptr_t forwardAddress); + + bool CheckAndTriggerOldGC(); + + bool CheckAndTriggerCompressGC(); + + bool CheckAndTriggerNonMovableGC(); + + RegionFactory *GetRegionFactory() const + { + return regionFactory_; + } + + SnapShotSpace *GetSnapShotSpace() const + { + return snapshotSpace_; + } + + bool IsLive(TaggedObject *object) const + { + if (!ContainObject(object)) { + return false; + } + + // semi space + if (toSpace_->IsLive(object)) { + return true; + } + // old space + if (oldSpace_->IsLive(object)) { + return true; + } + // non movable space + if (nonMovableSpace_->IsLive(object)) { + return true; + } + // large object space + if (largeObjectSpace_->IsLive(object)) { + return true; + } + return false; + } + + bool ContainObject(TaggedObject *object) const + { + // semi space + if (toSpace_->ContainObject(object)) { + return true; + } + // old space + if (oldSpace_->ContainObject(object)) { + return true; + } + // non movable space + if (nonMovableSpace_->ContainObject(object)) { + return true; + } + // large object space + if (largeObjectSpace_->ContainObject(object)) { + return true; + } + + return false; + } + + void RecomputeLimits(); + + const MemController *GetMemController() const + { + return memController_; + } + + void SetOldSpaceAllocLimit(size_t limit) + { + oldSpaceAllocLimit_ = limit; + } + + inline void ResetAppStartup(); + + size_t VerifyHeapObjects() const; + + inline void ClearSlotsRange(Region *current, uintptr_t freeStart, uintptr_t freeEnd); + +private: + EcmaVM *ecmaVm_{nullptr}; + SemiSpace *fromSpace_{nullptr}; + SemiSpace *toSpace_{nullptr}; + OldSpace *oldSpace_{nullptr}; + OldSpace *compressSpace_{nullptr}; + LargeObjectSpace *largeObjectSpace_{nullptr}; + SnapShotSpace *snapshotSpace_{nullptr}; + NonMovableSpace *nonMovableSpace_{nullptr}; + MarkStack *markStack_{nullptr}; + ProcessQueue *weakProcessQueue_{nullptr}; + SemiSpaceCollector *semiSpaceCollector_{nullptr}; + OldSpaceCollector *oldSpaceCollector_{nullptr}; + CompressCollector *compressCollector_{nullptr}; + EcmaHeapManager *heapManager_{nullptr}; + RegionFactory *regionFactory_{nullptr}; + HeapTracker *tracker_{nullptr}; + MemController *memController_{nullptr}; + ThreadPool *pool_{nullptr}; + size_t oldSpaceAllocLimit_{OLD_SPACE_LIMIT_BEGIN}; + + inline void SetMaximumCapacity(SemiSpace *space, size_t maximumCapacity); +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_H diff --git a/ecmascript/mem/heap_roots-inl.h b/ecmascript/mem/heap_roots-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..46160cf642bdbccdef1437cb613bf5c025fc7648 --- /dev/null +++ b/ecmascript/mem/heap_roots-inl.h @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_ROOTS_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_ROOTS_INL_H + +#include +#include "ecmascript/class_linker/program_object.h" +#include "ecmascript/ecma_module.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/ic/ic_handler.h" +#include "ecmascript/ic/proto_change_details.h" +#include "ecmascript/jobs/micro_job_queue.h" +#include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_arguments.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_async_function.h" +#include "ecmascript/js_dataview.h" +#include "ecmascript/js_date.h" +#include "ecmascript/js_float32_array.h" +#include "ecmascript/js_float64_array.h" +#include "ecmascript/js_for_in_iterator.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_generator_object.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_int16_array.h" +#include "ecmascript/js_int32_array.h" +#include "ecmascript/js_int8_array.h" +#include "ecmascript/js_map.h" +#include "ecmascript/js_map_iterator.h" +#include "ecmascript/js_native_object.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_regexp.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/js_string_iterator.h" +#include "ecmascript/js_typed_array.h" +#include "ecmascript/js_uint16_array.h" +#include "ecmascript/js_uint32_array.h" +#include "ecmascript/js_uint8_array.h" +#include "ecmascript/js_uint8_clamped_array.h" +#include "ecmascript/js_weak_container.h" +#include "ecmascript/mem/heap_roots.h" +#include "ecmascript/mem/mem.h" + +namespace panda::ecmascript { +void HeapRootManager::VisitVMRoots(const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor) const +{ + ecmaVm_->Iterate(visitor); + ecmaVm_->GetJSThread()->Iterate(visitor, rangeVisitor); +} + +template +// NOLINTNEXTLINE(readability-function-size) +void HeapRootManager::MarkObjectBody(TaggedObject *object, JSHClass *klass, const EcmaObjectRangeVisitor &visitor) +{ + // handle body + JSType type = klass->GetObjectType(); + switch (type) { + case JSType::JS_OBJECT: + case JSType::JS_ERROR: + case JSType::JS_EVAL_ERROR: + case JSType::JS_RANGE_ERROR: + case JSType::JS_REFERENCE_ERROR: + case JSType::JS_TYPE_ERROR: + case JSType::JS_URI_ERROR: + case JSType::JS_SYNTAX_ERROR: + case JSType::JS_ITERATOR: + JSObject::Cast(object)->Visitor(visitor); + break; + case JSType::JS_GLOBAL_OBJECT: + JSGlobalObject::Cast(object)->Visitor(visitor); + break; + case JSType::JS_FUNCTION_BASE: + JSFunctionBase::Cast(object)->Visitor(visitor); + break; + case JSType::JS_FUNCTION: + JSFunction::Cast(object)->Visitor(visitor); + break; + case JSType::JS_GENERATOR_FUNCTION: + JSGeneratorFunction::Cast(object)->Visitor(visitor); + break; + case JSType::JS_PROXY_REVOC_FUNCTION: + JSProxyRevocFunction::Cast(object)->Visitor(visitor); + break; + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + JSPromiseReactionsFunction::Cast(object)->Visitor(visitor); + break; + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + JSPromiseExecutorFunction::Cast(object)->Visitor(visitor); + break; + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + JSPromiseAllResolveElementFunction::Cast(object)->Visitor(visitor); + break; + case JSType::JS_ASYNC_FUNCTION: + JSAsyncFunction::Cast(object)->Visitor(visitor); + break; + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + JSAsyncAwaitStatusFunction::Cast(object)->Visitor(visitor); + break; + case JSType::JS_REG_EXP: + JSRegExp::Cast(object)->Visitor(visitor); + break; + case JSType::JS_SET: + JSSet::Cast(object)->Visitor(visitor); + break; + case JSType::JS_MAP: + JSMap::Cast(object)->Visitor(visitor); + break; + case JSType::JS_WEAK_MAP: + JSWeakMap::Cast(object)->Visitor(visitor); + break; + case JSType::JS_WEAK_SET: + JSWeakSet::Cast(object)->Visitor(visitor); + break; + case JSType::JS_DATE: + JSDate::Cast(object)->Visitor(visitor); + break; + case JSType::JS_FORIN_ITERATOR: + JSForInIterator::Cast(object)->Visitor(visitor); + break; + case JSType::JS_MAP_ITERATOR: + JSMapIterator::Cast(object)->Visitor(visitor); + break; + case JSType::JS_SET_ITERATOR: + JSSetIterator::Cast(object)->Visitor(visitor); + break; + case JSType::JS_ARRAY_ITERATOR: + JSArrayIterator::Cast(object)->Visitor(visitor); + break; + case JSType::JS_STRING_ITERATOR: + JSStringIterator::Cast(object)->Visitor(visitor); + break; + case JSType::JS_ARRAY_BUFFER: + JSArrayBuffer::Cast(object)->Visitor(visitor); + break; + case JSType::JS_PROMISE: + JSPromise::Cast(object)->Visitor(visitor); + break; + case JSType::JS_DATA_VIEW: + JSDataView::Cast(object)->Visitor(visitor); + break; + case JSType::JS_BOUND_FUNCTION: + JSBoundFunction::Cast(object)->Visitor(visitor); + break; + case JSType::JS_ARGUMENTS: + JSArguments::Cast(object)->Visitor(visitor); + break; + case JSType::JS_GENERATOR_OBJECT: + JSGeneratorObject::Cast(object)->Visitor(visitor); + break; + case JSType::JS_ASYNC_FUNC_OBJECT: + JSAsyncFuncObject::Cast(object)->Visitor(visitor); + break; + case JSType::JS_ARRAY: + JSArray::Cast(object)->Visitor(visitor); + break; + case JSType::JS_TYPED_ARRAY: + JSTypedArray::Cast(object)->Visitor(visitor); + break; + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(object)->Visitor(visitor); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(object)->Visitor(visitor); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(object)->Visitor(visitor); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(object)->Visitor(visitor); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(object)->Visitor(visitor); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(object)->Visitor(visitor); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(object)->Visitor(visitor); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(object)->Visitor(visitor); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(object)->Visitor(visitor); + break; + case JSType::JS_PRIMITIVE_REF: + JSPrimitiveRef::Cast(object)->Visitor(visitor); + break; + case JSType::JS_PROXY: + JSProxy::Cast(object)->Visitor(visitor); + break; + case JSType::HCLASS: + // semi gc is not needed to visit dyn class + if (gc_type != GCType::SEMI_GC) { + JSHClass::Cast(object)->Visitor(visitor); + } + break; + case JSType::STRING: + case JSType::JS_NATIVE_POINTER: + break; + case JSType::TAGGED_ARRAY: + case JSType::TAGGED_DICTIONARY: + TaggedArray::Cast(object)->Visitor(visitor); + break; + case JSType::GLOBAL_ENV: + GlobalEnv::Cast(object)->Visitor(visitor); + break; + case JSType::ACCESSOR_DATA: + case JSType::INTERNAL_ACCESSOR: + AccessorData::Cast(object)->Visitor(visitor); + break; + case JSType::SYMBOL: + JSSymbol::Cast(object)->Visitor(visitor); + break; + case JSType::OBJECT_WRAPPER: + ObjectWrapper::Cast(object)->Visitor(visitor); + break; + case JSType::JS_GENERATOR_CONTEXT: + GeneratorContext::Cast(object)->Visitor(visitor); + break; + case JSType::PROTOTYPE_HANDLER: + PrototypeHandler::Cast(object)->Visitor(visitor); + break; + case JSType::TRANSITION_HANDLER: + TransitionHandler::Cast(object)->Visitor(visitor); + break; + case JSType::PROPERTY_BOX: + PropertyBox::Cast(object)->Visitor(visitor); + break; + case JSType::PROTO_CHANGE_MARKER: + break; + case JSType::PROTOTYPE_INFO: + ProtoChangeDetails::Cast(object)->Visitor(visitor); + break; + case JSType::TEMPLATE_MAP: + UNREACHABLE(); + break; + case JSType::PROMISE_CAPABILITY: + PromiseCapability::Cast(object)->Visitor(visitor); + break; + case JSType::PROMISE_RECORD: + PromiseRecord::Cast(object)->Visitor(visitor); + break; + case JSType::RESOLVING_FUNCTIONS_RECORD: + ResolvingFunctionsRecord::Cast(object)->Visitor(visitor); + break; + case JSType::PROMISE_REACTIONS: + PromiseReaction::Cast(object)->Visitor(visitor); + break; + case JSType::PROMISE_ITERATOR_RECORD: + PromiseIteratorRecord::Cast(object)->Visitor(visitor); + break; + case JSType::MICRO_JOB_QUEUE: + job::MicroJobQueue::Cast(object)->Visitor(visitor); + break; + case JSType::PENDING_JOB: + job::PendingJob::Cast(object)->Visitor(visitor); + break; + case JSType::FUNCTION_EXTRA_INFO: + JSFunctionExtraInfo::Cast(object)->Visitor(visitor); + break; + case JSType::COMPLETION_RECORD: + CompletionRecord::Cast(object)->Visitor(visitor); + break; + case JSType::ECMA_MODULE: + EcmaModule::Cast(object)->Visitor(visitor); + break; + case JSType::PROGRAM: + Program::Cast(object)->Visitor(visitor); + break; + case JSType::LEXICAL_FUNCTION: + LexicalFunction::Cast(object)->Visitor(visitor); + break; + case JSType::JS_NATIVE_OBJECT: + JSNativeObject::Cast(object)->Visitor(visitor); + break; + default: + UNREACHABLE(); + } +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_ROOTS_INL_H diff --git a/ecmascript/mem/heap_roots.h b/ecmascript/mem/heap_roots.h new file mode 100644 index 0000000000000000000000000000000000000000..cc4d233b5029ee6b5d4b92b9f415f7e4eaa5747b --- /dev/null +++ b/ecmascript/mem/heap_roots.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_ROOTS_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_ROOTS_H + +#include +#include "ecmascript/mem/slots.h" + +namespace panda::ecmascript { +class EcmaVM; +class JSHClass; + +enum class Root { + ROOT_FRAME, + ROOT_HANDLE, + ROOT_VM, + ROOT_STRING, +}; + +enum class GCType : size_t { SEMI_GC, OLD_GC }; + +using RootVisitor = std::function; +using RootRangeVisitor = std::function; +using EcmaObjectVisitor = std::function; +using EcmaObjectRangeVisitor = std::function; + +using WeakRootVisitor = std::function; + +class HeapRootManager { +public: + explicit HeapRootManager(EcmaVM *ecmaVm) : ecmaVm_(ecmaVm) {} + ~HeapRootManager() = default; + + inline void VisitVMRoots(const RootVisitor &visitor, const RootRangeVisitor &range_visitor) const; + template + inline void MarkObjectBody(TaggedObject *object, JSHClass *klass, const EcmaObjectRangeVisitor &visitor); + +private: + EcmaVM *ecmaVm_{nullptr}; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_ROOTS_H diff --git a/ecmascript/mem/mark_stack-inl.h b/ecmascript/mem/mark_stack-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..c69166e850876313fafa417158025e93d9cc5b23 --- /dev/null +++ b/ecmascript/mem/mark_stack-inl.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_MARK_STACK_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_MARK_STACK_INL_H + +#include "ecmascript/mem/mark_stack.h" +#include "ecmascript/mem/heap.h" + +namespace panda::ecmascript { +template +void ContinuousStack::BeginMarking(Heap *heap, ContinuousStack *other) +{ + heap_ = heap; + currentArea_ = other->currentArea_; + if (currentArea_ == nullptr) { + currentArea_ = heap_->GetRegionFactory()->AllocateArea(DEFAULT_MARK_STACK_SIZE); + } + ResetBegin(currentArea_->GetBegin(), currentArea_->GetEnd()); +} + +template +void ContinuousStack::FinishMarking(ContinuousStack *other) +{ + other->currentArea_ = currentArea_; + + while (!unusedList_.IsEmpty()) { + Area *node = unusedList_.PopBack(); + heap_->GetRegionFactory()->FreeArea(node); + } +} + +template +void ContinuousStack::Extend() +{ + auto area = heap_->GetRegionFactory()->AllocateArea(DEFAULT_MARK_STACK_SIZE); + areaList_.AddNode(currentArea_); + currentArea_ = area; + ResetBegin(currentArea_->GetBegin(), currentArea_->GetEnd()); +} + +template +void ContinuousStack::TearDown() +{ + if (currentArea_ != nullptr) { + heap_->GetRegionFactory()->FreeArea(currentArea_); + currentArea_ = nullptr; + } +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_MARK_STACK_INL_H diff --git a/ecmascript/mem/mark_stack.h b/ecmascript/mem/mark_stack.h new file mode 100644 index 0000000000000000000000000000000000000000..621ae4d0e6aea6600189e4fe2eb393ec2dfb85ee --- /dev/null +++ b/ecmascript/mem/mark_stack.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_MARK_STACK_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_MARK_STACK_H + +#include "ecmascript/mem/ecma_list.h" +#include "ecmascript/mem/space.h" +#include "ecmascript/mem/area.h" +#include "ecmascript/mem/region_factory.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda { +namespace ecmascript { +class Stack { +public: + Stack() = default; + virtual ~Stack() = default; + NO_COPY_SEMANTIC(Stack); + NO_MOVE_SEMANTIC(Stack); + uintptr_t GetBegin() const + { + return begin_; + } + + uintptr_t PopBackChecked() + { + if (UNLIKELY(top_ <= reinterpret_cast(begin_))) { + return 0; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return *--top_; + } + + void PushBackUnchecked(uintptr_t obj) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *top_++ = obj; + } + + uintptr_t PopBackUnchecked() + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return *--top_; + } + + bool PushBackChecked(uintptr_t obj) + { + if (UNLIKELY(top_ >= end_)) { + return false; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *top_++ = obj; + return true; + } + + bool IsEmpty() const + { + return top_ == reinterpret_cast(begin_); + } + + void ResetBegin(uintptr_t begin, uintptr_t end) + { + begin_ = begin; + top_ = reinterpret_cast(begin); + end_ = reinterpret_cast(end); + } + + void ResetTop(uintptr_t begin, uintptr_t end) + { + begin_ = begin; + top_ = end_ = reinterpret_cast(end); + } + +private: + template + friend class ContinuousStack; + friend class WorkNode; + uintptr_t begin_{0}; + uintptr_t *end_{nullptr}; + uintptr_t *top_{nullptr}; +}; + +template +class ContinuousStack : public Stack { +public: + ContinuousStack() = default; + explicit ContinuousStack(Heap *heap) : heap_(heap) {} + ~ContinuousStack() override = default; + NO_COPY_SEMANTIC(ContinuousStack); + NO_MOVE_SEMANTIC(ContinuousStack); + + inline void BeginMarking(Heap *heap, ContinuousStack *other); + inline void FinishMarking(ContinuousStack *other); + + T *PopBack() + { + if (UNLIKELY(top_ <= reinterpret_cast(begin_))) { + if (!areaList_.IsEmpty()) { + unusedList_.AddNode(currentArea_); + Area *last = areaList_.PopBack(); + currentArea_ = last; + ResetTop(currentArea_->GetBegin(), currentArea_->GetEnd()); + } else { + return nullptr; + } + } + return reinterpret_cast(PopBackUnchecked()); + } + + void PushBack(T *obj) + { + if (UNLIKELY(top_ >= end_)) { + Extend(); + } + PushBackUnchecked(ToUintPtr(obj)); + } + + inline void TearDown(); + +private: + inline void Extend(); + + Heap *heap_{nullptr}; + Area *currentArea_{nullptr}; + EcmaList areaList_{}; + EcmaList unusedList_{}; +}; + +using MarkStack = ContinuousStack; +using ProcessQueue = ContinuousStack; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_MARK_STACK_H diff --git a/ecmascript/mem/mark_word.h b/ecmascript/mem/mark_word.h new file mode 100644 index 0000000000000000000000000000000000000000..3857e4e6dd3672100a4ef541b019a4462bc3f20c --- /dev/null +++ b/ecmascript/mem/mark_word.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_MARK_WORD_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_MARK_WORD_H + +#include +#include + +#include "utils/bit_field.h" +#include "libpandabase/mem/mem.h" + +namespace panda { +namespace ecmascript { +class TaggedObject; +class JSHClass; + +using MarkWordType = uint64_t; + +class MarkWord { +public: + // ForwardingAddress mark, this object has been moved and the address points to the newly allocated space. + static constexpr MarkWordType TAG_MARK_BIT = 0x02ULL; + + explicit MarkWord(TaggedObject *header) + { + value_ = reinterpret_cast *>(header)->load(std::memory_order_acquire); + } + ~MarkWord() = default; + NO_COPY_SEMANTIC(MarkWord); + NO_MOVE_SEMANTIC(MarkWord); + + bool IsForwardingAddress() + { + return (value_ & TAG_MARK_BIT) != 0; + } + + TaggedObject *ToForwardingAddress() + { + return reinterpret_cast(value_ & (~TAG_MARK_BIT)); + } + + static MarkWordType FromForwardingAddress(MarkWordType forwardAddress) + { + return forwardAddress | TAG_MARK_BIT; + } + + MarkWordType GetValue() const + { + return value_; + } + + JSHClass *GetJSHClass() const + { + return reinterpret_cast(value_ & (~TAG_MARK_BIT)); + } + +private: + MarkWordType value_{0}; +}; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_MARK_WORD_H diff --git a/ecmascript/mem/mem.h b/ecmascript/mem/mem.h new file mode 100644 index 0000000000000000000000000000000000000000..ca29ae88b50e0f4da467d9010f38305f82e9defd --- /dev/null +++ b/ecmascript/mem/mem.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_MEM_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_MEM_H + +#include + +#include "ecmascript/mem/tagged_object.h" +#include "libpandabase/mem/mem.h" +#include "libpandabase/utils/logger.h" +#include "mem/gc/bitmap.h" + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage, bugprone-lambda-function-name) +#define LOG_ECMA_MEM(type) LOG(type, ECMASCRIPT) << __func__ << " Line:" << __LINE__ << " " + +namespace panda::ecmascript { +enum class MemAlignment : uint8_t { + MEM_ALIGN_OBJECT = 8, + MEM_ALIGN_REGION = 16, +}; + +static constexpr size_t DEFAULT_SEMI_SPACE_SIZE = 1024 * 1024; +static constexpr size_t MIN_AllOC_LIMIT_GROWING_STEP = 2 * 1024 * 1024; +static constexpr size_t SEMI_SPACE_SIZE_4M = 4 * 1024 * 1024; +static constexpr size_t SEMI_SPACE_SIZE_8M = 8 * 1024 * 1024; +static constexpr size_t MAX_SEMI_SPACE_SIZE_STARTUP = 16 * 1024 * 1024; + +static constexpr size_t OLD_SPACE_LIMIT_BEGIN = 10 * 1024 * 1024; + +static constexpr size_t DEFAULT_OLD_SPACE_SIZE = 2 * 1024 * 1024; +static constexpr size_t MAX_OLD_SPACE_SIZE = 256 * 1024 * 1024; + +static constexpr size_t DEFAULT_NON_MOVABLE_SPACE_SIZE = 2 * 1024 * 1024; +static constexpr size_t MAX_NON_MOVABLE_SPACE_SIZE = 256 * 1024 * 1024; +static constexpr size_t DEFAULT_NON_MOVABLE_SPACE_LIMIT = 4 * 1024 * 1024; + +static constexpr size_t REGION_SIZE_LOG2 = 18U; +static constexpr size_t DEFAULT_SNAPSHOT_SPACE_SIZE = 1U << REGION_SIZE_LOG2; +static constexpr size_t MAX_SNAPSHOT_SPACE_SIZE = 8 * 1024 * 1024; + +static constexpr size_t MAX_HEAP_SIZE = 256 * 1024 * 1024; + +static constexpr size_t DEFAULT_REGION_SIZE = 1U << REGION_SIZE_LOG2; +static constexpr size_t DEFAULT_REGION_MASK = DEFAULT_REGION_SIZE - 1; + +static constexpr size_t DEFAULT_MARK_STACK_SIZE = 4 * 1024; + +// Objects which are larger than half of the region size are large objects. +// Regular objects will be allocated on regular regions and migrated on spaces. +// They will never be moved to large object space. So we take half of a regular +// region as the border of regular objects. +static constexpr size_t MAX_32BIT_OBJECT_SPACE_SIZE = 1 * 1024 * 1024 * 1024; +static constexpr size_t MAX_REGULAR_HEAP_OBJECT_SIZE = 1U << (REGION_SIZE_LOG2 - 1); +static constexpr size_t MAX_LARGE_OBJECT_SIZE = 256 * 1024 * 1024; +static constexpr size_t MAX_LARGE_OBJECT_SPACE_SIZE = 256 * 1024 * 1024; +static constexpr size_t LARGE_BITMAP_MIN_SIZE = static_cast(MemAlignment::MEM_ALIGN_OBJECT) + << mem::Bitmap::LOG_BITSPERWORD; + +static constexpr size_t SMALL_OBJECT_SIZE = 8 * 1024; + +// internal allocator +static constexpr size_t CHUNK_ALIGN_SIZE = 4 * 1024; +static constexpr size_t MIN_CHUNK_AREA_SIZE = 4 * 1024; +static constexpr size_t MAX_CACHED_CHUNK_AREA_SIZE = 16 * 1024; +static constexpr size_t MAX_CHUNK_AREA_SIZE = 1 * 1024 * 1024; + +static constexpr uintptr_t PANDA_32BITS_HEAP_START_ADDRESS_256 = 256_KB; + +static os::memory::Mutex staticResourceLock_; + +template +constexpr inline bool IsAligned(T value, size_t alignment) +{ + return (value & (alignment - 1U)) == 0; +} + +template +inline T AlignDown(T x, size_t alignment) +{ + ASSERT(std::is_integral::value); + // alignment must be a power of two. + ASSERT(alignment != 0 && ((alignment & (alignment - 1U)) == 0)); + return x & ~(alignment - 1U); +} + +template +inline T AlignUp(T x, size_t alignment) +{ + ASSERT(std::is_integral::value); + return AlignDown(static_cast(x + alignment - 1U), alignment); +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_MEM_H diff --git a/ecmascript/mem/mem_controller.cpp b/ecmascript/mem/mem_controller.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff803ff2074670a45f6c025448ad99524bf973ff --- /dev/null +++ b/ecmascript/mem/mem_controller.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mem_controller.h" + +namespace panda::ecmascript { +MemController::MemController(bool isInAppStartup) : isInAppStartup_(isInAppStartup) {} + +double MemController::CalculateAllocLimit(size_t currentSize, size_t minSize, size_t maxSize, size_t newSpaceCapacity, + double factor) const +{ + const uint64_t limit = std::max(static_cast(currentSize * factor), + static_cast(currentSize) + MIN_AllOC_LIMIT_GROWING_STEP) + + SEMI_SPACE_SIZE_4M; + + const uint64_t limitAboveMinSize = std::max(limit, minSize); + const uint64_t halfToMaxSize = (static_cast(currentSize) + maxSize) / 2; + const auto result = static_cast(std::min(limitAboveMinSize, halfToMaxSize)); + return result; +} + +MemController *CreateMemController(std::string_view gcTriggerType) +{ + auto triggerType = GCTriggerType::INVALID_TRIGGER; + if (gcTriggerType == "no-gc-for-start-up") { + triggerType = GCTriggerType::NO_GC_FOR_STARTUP; + } else if (gcTriggerType == "heap-trigger") { + triggerType = GCTriggerType::HEAP_TRIGGER; + } + MemController *ret{nullptr}; + switch (triggerType) { // NOLINT(hicpp-multiway-paths-covered) + case GCTriggerType::NO_GC_FOR_STARTUP: + ret = new MemController(true); + break; + case GCTriggerType::HEAP_TRIGGER: + ret = new MemController(false); + break; + default: + break; + } + return ret; +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/mem_controller.h b/ecmascript/mem/mem_controller.h new file mode 100644 index 0000000000000000000000000000000000000000..f32ec351422bf0468092f0df683de806517c80cd --- /dev/null +++ b/ecmascript/mem/mem_controller.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_MEM_CONTROLLER_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_MEM_CONTROLLER_H + +#include "ecmascript/mem/mem.h" + +namespace panda::ecmascript { +constexpr static int MILLISECONDS_PER_SECOND = 1000; + +enum class GCTriggerType { + INVALID_TRIGGER, + NO_GC_FOR_STARTUP, // guarantee no gc for app startup + HEAP_TRIGGER, +}; + +using BytesAndDuration = std::pair; + +inline BytesAndDuration MakeBytesAndDuration(uint64_t bytes, double duration) +{ + return std::make_pair(bytes, duration); +} + +class MemController { +public: + explicit MemController(bool isInAppStartUp = false); + MemController() = default; + ~MemController() = default; + NO_COPY_SEMANTIC(MemController); + NO_MOVE_SEMANTIC(MemController); + + double CalculateAllocLimit(size_t currentSize, size_t minSize, size_t maxSize, size_t newSpaceCapacity, + double factor) const; + + inline bool IsInAppStartup() const + { + return isInAppStartup_; + } + + void ResetAppStartup() + { + isInAppStartup_ = false; + } + +private: + bool isInAppStartup_{false}; +}; + +MemController *CreateMemController(std::string_view gcTriggerType); +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_MEM_CONTROLLER_H diff --git a/ecmascript/mem/old_space_collector-inl.h b/ecmascript/mem/old_space_collector-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..d99f59cf778a0963b6f49ebbad114b4d28acb122 --- /dev/null +++ b/ecmascript/mem/old_space_collector-inl.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_INL_H + +#include "ecmascript/mem/old_space_collector.h" +#include "ecmascript/mem/mem.h" +#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/region.h" +#include "ecmascript/mem/mark_word.h" +#include "ecmascript/js_hclass-inl.h" + +namespace panda::ecmascript { +void OldSpaceCollector::MarkObject(TaggedObject *object) +{ + Region *objectRegion = Region::ObjectAddressToRange(object); + + auto markBitmap = objectRegion->GetMarkBitmap(); + if (!markBitmap->Test(object)) { + markBitmap->Set(object); + markStack_.PushBack(object); + } +} + +void OldSpaceCollector::RecordWeakReference(JSTaggedType *ref) +{ + Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast(ref)); + if (!objectRegion->InYoungGeneration()) { + weakProcessQueue_.PushBack(ref); + } +} + +void OldSpaceCollector::FreeLiveRange(FreeListAllocator &allocator, Region *current, uintptr_t freeStart, + uintptr_t freeEnd) +{ + allocator.Free(freeStart, freeEnd); + freeSize_ += freeEnd - freeStart; + heap_->ClearSlotsRange(current, freeStart, freeEnd); +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_INL_H diff --git a/ecmascript/mem/old_space_collector.cpp b/ecmascript/mem/old_space_collector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..285aa5576284f4a63cf4edf0cfdbd40b03bb550d --- /dev/null +++ b/ecmascript/mem/old_space_collector.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/old_space_collector-inl.h" + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/mem/clock_scope.h" +#include "ecmascript/mem/ecma_heap_manager.h" +#include "ecmascript/mem/heap-inl.h" +#include "ecmascript/mem/heap_roots-inl.h" +#include "ecmascript/mem/mark_stack.h" +#include "ecmascript/mem/mem.h" +#include "ecmascript/mem/space-inl.h" + +namespace panda::ecmascript { +OldSpaceCollector::OldSpaceCollector(Heap *heap) : heap_(heap), rootManager_(heap->GetEcmaVM()) {} + +void OldSpaceCollector::RunPhases() +{ + trace::ScopedTrace scoped_trace("OldSpaceCollector::RunPhases"); + [[maybe_unused]] ClockScope clock("OldSpaceCollector::RunPhases"); + InitializePhase(); + MarkingPhase(); + SweepPhases(); + FinishPhase(); + heap_->GetEcmaVM()->GetEcmaGCStats()->StatisticOldCollector(clock.GetPauseTime(), freeSize_, oldSpaceCommitSize_, + nonMoveSpaceCommitSize_); +} + +void OldSpaceCollector::InitializePhase() +{ + markStack_.BeginMarking(heap_, heap_->GetMarkStack()); + weakProcessQueue_.BeginMarking(heap_, heap_->GetProcessQueue()); + auto heapManager = heap_->GetHeapManager(); + oldSpaceAllocator_.Swap(heapManager->GetOldSpaceAllocator()); + nonMovableAllocator_.Swap(heapManager->GetNonMovableSpaceAllocator()); + heap_->EnumerateRegions([](Region *current) { + // ensure mark bitmap + auto bitmap = current->GetMarkBitmap(); + if (bitmap == nullptr) { + current->GetOrCreateMarkBitmap(); + } else { + bitmap->ClearAllBits(); + } + }); + freeSize_ = 0; + largeSpaceFreeSize_ = 0; + oldSpaceCommitSize_ = heap_->GetOldSpace()->GetCommittedSize(); + nonMoveSpaceCommitSize_ = heap_->GetNonMovableSpace()->GetCommittedSize(); +} + +void OldSpaceCollector::FinishPhase() +{ + // swap + markStack_.FinishMarking(heap_->GetMarkStack()); + weakProcessQueue_.FinishMarking(heap_->GetProcessQueue()); + auto heapManager = heap_->GetHeapManager(); + heapManager->GetOldSpaceAllocator().Swap(oldSpaceAllocator_); + heapManager->GetNonMovableSpaceAllocator().Swap(nonMovableAllocator_); +} + +void OldSpaceCollector::MarkingPhase() +{ + trace::ScopedTrace scoped_trace("OldSpaceCollector::MarkingPhase"); + RootVisitor gcMarkYoung = [this]([[maybe_unused]] Root type, ObjectSlot slot) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + MarkObject(value.GetTaggedObject()); + } + }; + RootRangeVisitor gcMarkRangeYoung = [this]([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + MarkObject(value.GetTaggedObject()); + } + } + }; + rootManager_.VisitVMRoots(gcMarkYoung, gcMarkRangeYoung); + + ProcessMarkStack(); +} + +void OldSpaceCollector::ProcessMarkStack() +{ + while (true) { + auto obj = markStack_.PopBack(); + if (UNLIKELY(obj == nullptr)) { + break; + } + auto jsHclass = obj->GetClass(); + // mark dynClass + MarkObject(jsHclass); + + rootManager_.MarkObjectBody( + obj, jsHclass, [this]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsWeak()) { + RecordWeakReference(reinterpret_cast(slot.SlotAddress())); + continue; + } + if (value.IsHeapObject()) { + MarkObject(value.GetTaggedObject()); + } + } + }); + } +} + +void OldSpaceCollector::SweepSpace(Space *space, FreeListAllocator &allocator) +{ + allocator.RebuildFreeList(); + space->EnumerateRegions([this, &allocator](Region *current) { + auto markBitmap = current->GetMarkBitmap(); + ASSERT(markBitmap != nullptr); + uintptr_t freeStart = current->GetBegin(); + markBitmap->IterateOverMarkedChunks([this, ¤t, &freeStart, &allocator](void *mem) { + ASSERT(current->InRange(ToUintPtr(mem))); + auto header = reinterpret_cast(mem); + auto klass = header->GetClass(); + JSType jsType = klass->GetObjectType(); + auto size = klass->SizeFromJSHClass(jsType, header); + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + + uintptr_t freeEnd = ToUintPtr(mem); + if (freeStart != freeEnd) { + FreeLiveRange(allocator, current, freeStart, freeEnd); + } + freeStart = freeEnd + size; + }); + uintptr_t freeEnd = current->GetEnd(); + if (freeStart != freeEnd) { + FreeLiveRange(allocator, current, freeStart, freeEnd); + } + }); +} + +void OldSpaceCollector::SweepSpace(LargeObjectSpace *space) +{ + Region *currentRegion = space->GetRegionList().GetFirst(); + + while (currentRegion != nullptr) { + Region *next = currentRegion->GetNext(); + auto markBitmap = currentRegion->GetMarkBitmap(); + bool isMarked = false; + markBitmap->IterateOverMarkedChunks([&isMarked]([[maybe_unused]] void *mem) { isMarked = true; }); + if (!isMarked) { + space->GetRegionList().RemoveNode(currentRegion); + largeSpaceFreeSize_ += currentRegion->GetCapacity(); + space->ClearAndFreeRegion(currentRegion); + } + currentRegion = next; + } +} + +void OldSpaceCollector::SweepPhases() +{ + trace::ScopedTrace scoped_trace("OldSpaceCollector::SweepPhases"); + // process weak reference + while (true) { + auto obj = weakProcessQueue_.PopBack(); + if (UNLIKELY(obj == nullptr)) { + break; + } + ObjectSlot slot(ToUintPtr(obj)); + JSTaggedValue value(slot.GetTaggedType()); + auto header = value.GetTaggedWeakRef(); + + Region *objectRegion = Region::ObjectAddressToRange(header); + auto markBitmap = objectRegion->GetMarkBitmap(); + if (!markBitmap->Test(header)) { + slot.Update(static_cast(JSTaggedValue::Undefined().GetRawData())); + } + } + + auto stringTable = heap_->GetEcmaVM()->GetEcmaStringTable(); + WeakRootVisitor gcUpdateWeak = [](TaggedObject *header) { + Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast(header)); + if (objectRegion->InYoungGeneration()) { + return header; + } + + auto markBitmap = objectRegion->GetMarkBitmap(); + if (markBitmap->Test(header)) { + return header; + } + return reinterpret_cast(ToUintPtr(nullptr)); + }; + stringTable->SweepWeakReference(gcUpdateWeak); + heap_->GetEcmaVM()->ProcessReferences(gcUpdateWeak); + + SweepSpace(const_cast(heap_->GetOldSpace()), oldSpaceAllocator_); + SweepSpace(const_cast(heap_->GetNonMovableSpace()), nonMovableAllocator_); + SweepSpace(heap_->GetLargeObjectSpace()); +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/old_space_collector.h b/ecmascript/mem/old_space_collector.h new file mode 100644 index 0000000000000000000000000000000000000000..53d9b477702d0d3c5d48a384e96521448775310f --- /dev/null +++ b/ecmascript/mem/old_space_collector.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_OLD_SAPACE_COLLECTOR_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_OLD_SAPACE_COLLECTOR_H + +#include "ecmascript/mem/mem.h" +#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/allocator.h" +#include "ecmascript/mem/mark_stack-inl.h" +#include "ecmascript/mem/mark_word.h" +#include "ecmascript/mem/slots.h" +#include "ecmascript/mem/heap_roots.h" +#include "ecmascript/mem/remembered_set.h" +#include "ecmascript/mem/semi_space_collector.h" + +namespace panda { +namespace ecmascript { +class Heap; +class JSHClass; + +class OldSpaceCollector : public GarbageCollector { +public: + explicit OldSpaceCollector(Heap *heap); + ~OldSpaceCollector() override = default; + NO_COPY_SEMANTIC(OldSpaceCollector); + NO_MOVE_SEMANTIC(OldSpaceCollector); + void RunPhases(); + + Heap *GetHeap() const + { + return heap_; + } + +private: + void InitializePhase(); + void MarkingPhase(); + void SweepPhases(); + void FinishPhase(); + + void ProcessMarkStack(); + void MarkObjectBody(TaggedObject *object, JSHClass *klass, const EcmaObjectRangeVisitor &visitor); + + inline void MarkObject(TaggedObject *object); + inline void FreeLiveRange(FreeListAllocator &allocator, Region *current, uintptr_t freeStart, uintptr_t freeEnd); + inline void RecordWeakReference(JSTaggedType *ref); + void SweepSpace(Space *space, FreeListAllocator &allocator); + void SweepSpace(LargeObjectSpace *space); // Only sweep large space. + + Heap *heap_; + HeapRootManager rootManager_; + MarkStack markStack_; + ProcessQueue weakProcessQueue_; + FreeListAllocator oldSpaceAllocator_{}; + FreeListAllocator nonMovableAllocator_{}; + size_t freeSize_{0}; + size_t largeSpaceFreeSize_ = 0; + size_t oldSpaceCommitSize_ = 0; + size_t nonMoveSpaceCommitSize_ = 0; +}; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_OLD_SAPACE_COLLECTOR_H diff --git a/ecmascript/mem/region.h b/ecmascript/mem/region.h new file mode 100644 index 0000000000000000000000000000000000000000..2c16971de761ff25c672e17c9f30a9e7faad56a8 --- /dev/null +++ b/ecmascript/mem/region.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_REGION_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_REGION_H + +#include "ecmascript/mem/mem.h" +#include "mem/gc/bitmap.h" + +namespace panda { +using RangeBitmap = mem::MemBitmap(ecmascript::MemAlignment::MEM_ALIGN_OBJECT)>; + +namespace ecmascript { +class Space; +class RememberedSet; + +enum RegionFlags { + NEVER_EVACUATE = 1, + // NOLINTNEXTLINE(hicpp-signed-bitwise) + HAS_AGE_MARK = 1 << 1, + // NOLINTNEXTLINE(hicpp-signed-bitwise) + BELOW_AGE_MARK = 1 << 2, + // NOLINTNEXTLINE(hicpp-signed-bitwise) + IS_IN_YOUNG_GENERATION = 1 << 3, + // NOLINTNEXTLINE(hicpp-signed-bitwise) + IS_IN_SNAPSHOT_GENERATION = 1 << 4, + // NOLINTNEXTLINE(hicpp-signed-bitwise) + IS_LARGE_OBJECT = 1 << 5, + // NOLINTNEXTLINE(hicpp-signed-bitwise) + IS_IN_OLD_GENERATION = 1 << 6, + // NOLINTNEXTLINE(hicpp-signed-bitwise) + IS_IN_YOUNG_OR_OLD_GENERATION = IS_IN_YOUNG_GENERATION | IS_IN_OLD_GENERATION, + // NOLINTNEXTLINE(hicpp-signed-bitwise) + IS_INVALID = 1 << 7, +}; + +class Region { +public: + Region(Space *space, uintptr_t allocateBase, uintptr_t begin, uintptr_t end) + : space_(space), + flags_(0), + allocateBase_(allocateBase), + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + begin_(begin), + end_(end), + highWaterMark_(end) + { + } + ~Region() = default; + NO_COPY_SEMANTIC(Region); + NO_MOVE_SEMANTIC(Region); + + void LinkNext(Region *next) + { + next_ = next; + } + + Region *GetNext() const + { + return next_; + } + + void LinkPrev(Region *prev) + { + prev_ = prev; + } + + Region *GetPrev() const + { + return prev_; + } + + uintptr_t GetBegin() const + { + return begin_; + } + + uintptr_t GetEnd() const + { + return end_; + } + + size_t GetCapacity() const + { + return end_ - reinterpret_cast(this); + } + + size_t GetSize() const + { + return end_ - begin_; + } + + Space *GetSpace() const + { + return space_; + } + + void SetFlag(RegionFlags flag) + { + flags_ |= flag; + } + + void ClearFlag(RegionFlags flag) + { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + flags_ &= ~flag; + } + + bool IsFlagSet(RegionFlags flag) const + { + return (flags_ & flag) != 0; + } + + RangeBitmap *GetMarkBitmap() + { + return markBitmap_; + } + + RangeBitmap *GetOrCreateMarkBitmap() + { + if (UNLIKELY(markBitmap_ == nullptr)) { + markBitmap_ = CreateMarkBitmap(); + } + return markBitmap_; + } + + RememberedSet *GetCrossRegionRememberedSet() + { + return crossRegionSet_; + } + + RememberedSet *GetOrCreateCrossRegionRememberedSet() + { + if (UNLIKELY(crossRegionSet_ == nullptr)) { + crossRegionSet_ = CreateRememberedSet(); + } + return crossRegionSet_; + } + + RememberedSet *GetOldToNewRememberedSet() + { + return oldToNewSet_; + } + + RememberedSet *GetOrCreateOldToNewRememberedSet() + { + if (UNLIKELY(oldToNewSet_ == nullptr)) { + oldToNewSet_ = CreateRememberedSet(); + } + return oldToNewSet_; + } + + static Region *ObjectAddressToRange(TaggedObject *obj) + { + return reinterpret_cast(ToUintPtr(obj) & ~DEFAULT_REGION_MASK); + } + + bool InYoungGeneration() const + { + return IsFlagSet(RegionFlags::IS_IN_YOUNG_GENERATION); + } + + bool InYoungAndOldGeneration() const + { + return IsFlagSet(RegionFlags::IS_IN_YOUNG_OR_OLD_GENERATION); + } + + bool HasAgeMark() const + { + return IsFlagSet(RegionFlags::HAS_AGE_MARK); + } + + bool BelowAgeMark() const + { + return IsFlagSet(RegionFlags::BELOW_AGE_MARK); + } + + bool InRange(uintptr_t address) + { + return address >= begin_ && address <= end_; + } + + RangeBitmap *CreateMarkBitmap(); + RememberedSet *CreateRememberedSet(); + inline void InsertCrossRegionRememberedSet(uintptr_t addr); + inline void InsertOldToNewRememberedSet(uintptr_t addr); + + uintptr_t GetAllocateBase() const + { + return allocateBase_; + } + + size_t GetAllocatedBytes(uintptr_t top = 0) + { + ASSERT(top == 0 || InRange(top)); + return (top == 0) ? (highWaterMark_ - begin_) : (top - begin_); + } + + void SetHighWaterMark(uintptr_t mark) + { + highWaterMark_ = mark; + } + +private: + Space *space_; + uintptr_t flags_; // Memory alignment, only low 32bits are used now + uintptr_t allocateBase_; + uintptr_t begin_; + uintptr_t end_; + uintptr_t highWaterMark_; + Region *next_{nullptr}; + Region *prev_{nullptr}; + RangeBitmap *markBitmap_{nullptr}; + RememberedSet *crossRegionSet_{nullptr}; + RememberedSet *oldToNewSet_{nullptr}; + friend class SnapShot; +}; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_REGION_H diff --git a/ecmascript/mem/region_factory.cpp b/ecmascript/mem/region_factory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d990122b33f38fd9dc240318fd572ac268e234fa --- /dev/null +++ b/ecmascript/mem/region_factory.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MM_REGION_FACTORY_H +#define PANDA_RUNTIME_ECMASCRIPT_MM_REGION_FACTORY_H + +#include "ecmascript/mem/region_factory.h" + +#include "ecmascript/mem/mark_stack.h" +#include "ecmascript/mem/region.h" +#include "libpandabase/mem/pool_manager.h" +#include "os/mem.h" + +namespace panda::ecmascript { +Region *RegionFactory::AllocateAlignedRegion(Space *space, size_t capacity) +{ + if (capacity == 0) { + LOG_ECMA_MEM(FATAL) << "capacity must have a size bigger than 0"; + UNREACHABLE(); + } + size_t commitSize = capacity; + + auto pool = PoolManager::GetMmapMemPool()->AllocPool(commitSize, panda::SpaceType::SPACE_TYPE_OBJECT, + AllocatorType::RUNSLOTS_ALLOCATOR, nullptr); + void *mapMem = pool.GetMem(); + if (mapMem == nullptr) { + LOG_ECMA_MEM(FATAL) << "pool is empty"; + UNREACHABLE(); + } + IncreaseMemoryUsage(capacity); + + uintptr_t mem = ToUintPtr(mapMem); + // Check that the address is 256K byte aligned + LOG_IF(AlignUp(mem, PANDA_POOL_ALIGNMENT_IN_BYTES) != mem, FATAL, RUNTIME) << "region not align by 256KB"; + + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uintptr_t begin = AlignUp(mem + sizeof(Region), static_cast(MemAlignment::MEM_ALIGN_REGION)); + uintptr_t end = mem + capacity; + + return new (ToVoidPtr(mem)) Region(space, mem, begin, end); +} + +void RegionFactory::FreeRegion(Region *region) +{ + auto size = region->GetCapacity(); + DecreaseMemoryUsage(size); + PoolManager::GetMmapMemPool()->FreePool(ToVoidPtr(region->GetAllocateBase()), size); +} + +Area *RegionFactory::AllocateArea(size_t capacity) +{ + size_t headerSize = sizeof(Area); + if (capacity < headerSize) { + LOG_ECMA_MEM(FATAL) << "capacity must have a size not less than sizeof Area."; + UNREACHABLE(); + } + if (cachedArea_ != nullptr && capacity <= cachedArea_->GetSize()) { + auto result = cachedArea_; + cachedArea_ = nullptr; + return result; + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + void *mem = malloc(capacity); + if (mem == nullptr) { + LOG_ECMA_MEM(FATAL) << "malloc failed"; + UNREACHABLE(); + } + IncreaseMemoryUsage(capacity); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uintptr_t begin = reinterpret_cast(mem) + headerSize; + capacity -= headerSize; + return new (mem) Area(begin, capacity); +} + +void RegionFactory::FreeArea(Area *area) +{ + if (area == nullptr) { + return; + } + if (cachedArea_ == nullptr && area->GetSize() <= MAX_CACHED_CHUNK_AREA_SIZE) { + cachedArea_ = area; + return; + } + auto size = area->GetSize() + sizeof(Area); + DecreaseMemoryUsage(size); + os::memory::LockHolder lock(staticResourceLock_); + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + free(reinterpret_cast(area)); +} + +void *RegionFactory::AllocateWithMMap(size_t size) +{ + size = AlignUp(size, CHUNK_ALIGN_SIZE); + void *mem = panda::os::mem::MapRWAnonymousRaw(size); + if (mem == nullptr) { + LOG_ECMA_MEM(FATAL) << "mem is nullptr"; + UNREACHABLE(); + } + ASAN_UNPOISON_MEMORY_REGION(mem, size); + IncreaseMemoryUsage(size); + return mem; +} + +void RegionFactory::FreeWithMMap(void *mem, size_t size) +{ + DecreaseMemoryUsage(size); + os::mem::MmapDeleter(reinterpret_cast(mem), size); +} + +void *RegionFactory::Allocate(size_t size) +{ + if (size == 0) { + LOG_ECMA_MEM(FATAL) << "size must have a size bigger than 0"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + void *ptr = malloc(size); + if (ptr == nullptr) { + LOG_ECMA_MEM(FATAL) << "malloc failed"; + UNREACHABLE(); + } + IncreaseMemoryUsage(size); + return ptr; +} + +void RegionFactory::Free(void *mem, size_t size) +{ + if (mem == nullptr) { + return; + } + DecreaseMemoryUsage(size); + os::memory::LockHolder lock(staticResourceLock_); + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + free(mem); +} + +void *RegionFactory::AllocateBuffer(size_t size) +{ + if (size == 0) { + LOG_ECMA_MEM(FATAL) << "size must have a size bigger than 0"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + void *ptr = malloc(size); + if (ptr == nullptr) { + LOG_ECMA_MEM(FATAL) << "malloc failed"; + UNREACHABLE(); + } + return ptr; +} + +void RegionFactory::FreeBuffer(void *mem) +{ + if (mem == nullptr) { + return; + } + os::memory::LockHolder lock(staticResourceLock_); + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + free(mem); +} + +void RegionFactory::FreeBufferFunc(void *buffer, void* data) +{ + if (buffer == nullptr || data == nullptr) { + return; + } + RegionFactory* factory = reinterpret_cast(data); + factory->FreeBuffer(buffer); +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MM_REGION_FACTORY_H diff --git a/ecmascript/mem/region_factory.h b/ecmascript/mem/region_factory.h new file mode 100644 index 0000000000000000000000000000000000000000..927096609978f4b0d3249515e5fd286b2a5d4aae --- /dev/null +++ b/ecmascript/mem/region_factory.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_REGION_FACTORY_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_REGION_FACTORY_H + +#include + +#include "ecmascript/mem/mem.h" +#include "ecmascript/mem/area.h" +#include "libpandabase/utils/logger.h" + +namespace panda::ecmascript { +class Region; +class Space; +class Area; + +class RegionFactory { +public: + RegionFactory() = default; + virtual ~RegionFactory() + { + if (cachedArea_ != nullptr) { + FreeArea(cachedArea_); + cachedArea_ = nullptr; + } + } + + Region *AllocateAlignedRegion(Space *space, size_t capacity); + void FreeRegion(Region *region); + Area *AllocateArea(size_t capacity); + void FreeArea(Area *area); + void *AllocateWithMMap(size_t size); + void FreeWithMMap(void *mem, size_t size); + void *Allocate(size_t size); + void Free(void *mem, size_t size); + void *AllocateBuffer(size_t size); + void FreeBuffer(void *mem); + + static void FreeBufferFunc(void* buffer, void* data); + + // implemented by AllocateBuffer + template + std::enable_if_t, T *> New(Args &&... args) + { + void *p = AllocateBuffer(sizeof(T)); + if (UNLIKELY(p == nullptr)) { + return nullptr; + } + new (p) T(std::forward(args)...); // NOLINT(bugprone-throw-keyword-missing) + return reinterpret_cast(p); + } + + void IncreaseMemoryUsage(size_t bytes) + { + size_t current = memoryUsage_.fetch_add(bytes, std::memory_order_relaxed) + bytes; + size_t max = maxMemoryUsage_.load(std::memory_order_relaxed); + while (current > max && !maxMemoryUsage_.compare_exchange_weak(max, current, std::memory_order_relaxed)) { + } + } + + void DecreaseMemoryUsage(size_t bytes) + { + memoryUsage_.fetch_sub(bytes, std::memory_order_relaxed); + } + + size_t GetCurrentMemoryUsage() const + { + return memoryUsage_.load(std::memory_order_relaxed); + } + + size_t GetMaxMemoryUsage() const + { + return maxMemoryUsage_.load(std::memory_order_relaxed); + } + +private: + NO_COPY_SEMANTIC(RegionFactory); + NO_MOVE_SEMANTIC(RegionFactory); + + Area *cachedArea_{nullptr}; + std::atomic memoryUsage_{0}; + std::atomic maxMemoryUsage_{0}; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_REGION_FACTORY_H diff --git a/ecmascript/mem/remembered_set.h b/ecmascript/mem/remembered_set.h new file mode 100644 index 0000000000000000000000000000000000000000..88f5ccbf30f5fcd2da2bd25e49973d94c6493068 --- /dev/null +++ b/ecmascript/mem/remembered_set.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_REMEMBERED_SET_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_REMEMBERED_SET_H + +#include "ecmascript/mem/mem.h" +#include "mem/gc/bitmap.h" + +namespace panda::ecmascript { +enum class SlotStatus : bool { + KEEP_SLOT, + CLEAR_SLOT, +}; + +class RememberedSet : public mem::Bitmap { +public: + using RememberedWordType = uintptr_t; + static const size_t BYTESPERCHUNK = static_cast(MemAlignment::MEM_ALIGN_OBJECT); + + RememberedSet(uintptr_t begin_addr, size_t range_size, uintptr_t bitset_addr) + : mem::Bitmap(reinterpret_cast(bitset_addr), range_size / BYTESPERCHUNK), + begin_addr_(begin_addr) + { + } + ~RememberedSet() = default; + NO_COPY_SEMANTIC(RememberedSet); + NO_MOVE_SEMANTIC(RememberedSet); + + void Insert(uintptr_t address) + { + SetBit(AddrToBitOffset(address)); + } + + template + void IterateOverSetBits(VisitorType visitor) + { + IterateOverSetBitsInRange(0, Size(), visitor); + } + + template + void IterateOverMarkedChunks(MemVisitor visitor) + { + IterateOverSetBits( + [&visitor, this](size_t bit_offset) -> bool { return visitor(BitOffsetToAddr(bit_offset)); }); + } + + void Clear(uintptr_t address) + { + ClearBit(AddrToBitOffset(address)); + } + + void ClearRange(uintptr_t begin, uintptr_t end) + { + ClearBitsInRange(AddrToBitOffset(begin), AddrToBitOffset(end)); + } + + static size_t GetSizeInByte(size_t range_size) + { + return (range_size >> mem::Bitmap::LOG_BITSPERWORD) / BYTESPERCHUNK * sizeof(mem::Bitmap::BitmapWordType); + } + +private: + void *BitOffsetToAddr(size_t bit_offset) const + { + return ToVoidPtr(begin_addr_ + bit_offset * BYTESPERCHUNK); + } + + size_t AddrToBitOffset(uintptr_t addr) const + { + return (addr - begin_addr_) / BYTESPERCHUNK; + } + + uintptr_t begin_addr_; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_REMEMBERED_SET_H diff --git a/ecmascript/mem/semi_space_collector-inl.h b/ecmascript/mem/semi_space_collector-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..7af7f35ed1c987d8e92ee42e7b857598eb02e5ff --- /dev/null +++ b/ecmascript/mem/semi_space_collector-inl.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_INL_H + +#include "ecmascript/mem/semi_space_collector.h" +#include "ecmascript/mem/mem.h" +#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/region.h" +#include "ecmascript/mem/mark_word.h" +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/mem/semi_space_worker.h" + +namespace panda::ecmascript { +void SemiSpaceCollector::UpdatePromotedSlot(TaggedObject *object, ObjectSlot slot) +{ +#ifndef NDEBUG + JSTaggedValue value(slot.GetTaggedType()); + ASSERT(value.IsHeapObject()); + ASSERT(Region::ObjectAddressToRange(value.GetTaggedObject())->InYoungGeneration()); +#endif + Region *objectRegion = Region::ObjectAddressToRange(object); + ASSERT(!objectRegion->InYoungGeneration()); + objectRegion->InsertOldToNewRememberedSet(slot.SlotAddress()); +} + +void SemiSpaceCollector::RecordWeakReference(uint32_t threadId, JSTaggedType *ref) +{ + auto value = JSTaggedValue(*ref); + Region *objectRegion = Region::ObjectAddressToRange(value.GetTaggedWeakRef()); + if (objectRegion->InYoungGeneration()) { + workList_->PushWeakReference(threadId, ref); + } +} + +uintptr_t SemiSpaceCollector::AllocateOld(size_t size) +{ + os::memory::LockHolder lock(allocatorLock_); + uintptr_t result = oldSpaceAllocator_.Allocate(size); + if (UNLIKELY(result == 0)) { + if (!heap_->FillOldSpaceAndTryGC(&oldSpaceAllocator_, false)) { + return 0; + } + result = oldSpaceAllocator_.Allocate(size); + if (UNLIKELY(result == 0)) { + return 0; + } + } + return result; +} + +uintptr_t SemiSpaceCollector::AllocateYoung(size_t size) +{ + os::memory::LockHolder lock(allocatorLock_); + uintptr_t result = fromSpaceAllocator_.Allocate(size); + if (UNLIKELY(result == 0)) { + if (!heap_->FillNewSpaceAndTryGC(&fromSpaceAllocator_, false)) { + return 0; + } + result = fromSpaceAllocator_.Allocate(size); + if (UNLIKELY(result == 0)) { + return 0; + } + } + return result; +} + +bool SemiSpaceCollector::BlowAgeMark(uintptr_t address) +{ + return address < ageMark_; +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_INL_H diff --git a/ecmascript/mem/semi_space_collector.cpp b/ecmascript/mem/semi_space_collector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..46d05149d675610b81321643866f2947727fe9d3 --- /dev/null +++ b/ecmascript/mem/semi_space_collector.cpp @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/semi_space_collector-inl.h" + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/mem/clock_scope.h" +#include "ecmascript/mem/ecma_heap_manager.h" +#include "ecmascript/mem/heap-inl.h" +#include "ecmascript/mem/heap_roots-inl.h" +#include "ecmascript/mem/mark_stack.h" +#include "ecmascript/mem/mem.h" +#include "ecmascript/mem/space-inl.h" +#include "ecmascript/mem/tlab_allocator-inl.h" + +namespace panda::ecmascript { +SemiSpaceCollector::SemiSpaceCollector(Heap *heap, bool parallelGc) + : heap_(heap), rootManager_(heap->GetEcmaVM()), paralledGC_(parallelGc), markObject_(this) +{ + workList_ = new SemiSpaceWorker(heap_, heap_->GetThreadPool()->GetThreadNum()); +} + +SemiSpaceCollector::~SemiSpaceCollector() +{ + if (workList_ != nullptr) { + delete workList_; + workList_ = nullptr; + } +} + +void SemiSpaceCollector::RunPhases() +{ + trace::ScopedTrace scoped_trace("SemiSpaceCollector::RunPhases"); + [[maybe_unused]] ClockScope clock("SemiSpaceCollector::RunPhases"); + InitializePhase(); + ParallelMarkingPhase(); + SweepPhases(); + FinishPhase(); + heap_->GetEcmaVM()->GetEcmaGCStats()->StatisticSemiCollector(clock.GetPauseTime(), semiCopiedSize_, promotedSize_, + commitSize_); +} + +void SemiSpaceCollector::InitializePhase() +{ + heap_->GetThreadPool()->WaitTaskFinish(); + gcTime_++; + auto fromSpace = heap_->GetFromSpace(); + if (fromSpace->GetCommittedSize() == 0) { + heap_->InitializeFromSpace(); + } + fromSpaceAllocator_.Reset(fromSpace); + auto heapManager = heap_->GetHeapManager(); + oldSpaceAllocator_.Swap(heapManager->GetOldSpaceAllocator()); + ageMark_ = heap_->GetNewSpace()->GetAgeMark(); + heap_->FlipNewSpace(); + workList_->Initialize(); + promotedSize_ = 0; + semiCopiedSize_ = 0; + commitSize_ = heap_->GetFromSpace()->GetCommittedSize(); +} + +void SemiSpaceCollector::FinishPhase() +{ + // swap + const_cast(heap_->GetNewSpace())->Swap(const_cast(heap_->GetFromSpace())); + if (paralledGC_) { + heap_->GetThreadPool()->Submit([this]([[maybe_unused]] uint32_t threadId) -> bool { + const_cast(heap_->GetFromSpace())->ReclaimRegions(); + return true; + }); + } else { + const_cast(heap_->GetFromSpace())->ReclaimRegions(); + } + workList_->Finish(semiCopiedSize_, promotedSize_); + auto heapManager = heap_->GetHeapManager(); + heapManager->GetOldSpaceAllocator().Swap(oldSpaceAllocator_); + heapManager->GetNewSpaceAllocator().Swap(fromSpaceAllocator_); + + auto newSpace = heap_->GetNewSpace(); + heap_->SetNewSpaceAgeMark(fromSpaceAllocator_.GetTop()); + Region *last = newSpace->GetCurrentRegion(); + last->SetFlag(RegionFlags::HAS_AGE_MARK); + + newSpace->EnumerateRegions([&last](Region *current) { + if (current != last) { + current->SetFlag(RegionFlags::BELOW_AGE_MARK); + } + }); +} + +bool SemiSpaceCollector::ParallelHandleOldToNew(uint32_t threadId, Region *region) +{ + auto cb = [this, threadId](Region *current) { + auto rememberedSet = current->GetOldToNewRememberedSet(); + if (LIKELY(rememberedSet != nullptr)) { + rememberedSet->IterateOverMarkedChunks([&rememberedSet, this, threadId](void *mem) -> bool { + ObjectSlot slot(ToUintPtr(mem)); + JSTaggedValue value(slot.GetTaggedType()); + + if (value.IsWeak()) { + RecordWeakReference(threadId, reinterpret_cast(mem)); + } else if (value.IsHeapObject()) { + auto slotStatus = markObject_.MarkObject(threadId, value.GetTaggedObject(), slot); + if (slotStatus == SlotStatus::CLEAR_SLOT) { + // clear bit + rememberedSet->Clear(ToUintPtr(mem)); + } + } + return true; + }); + } + }; + heap_->EnumerateOldSpaceRegions(cb, region); + ProcessMarkStack(threadId); + return true; +} + +bool SemiSpaceCollector::ParallelHandleGlobalPool(uint32_t threadId) +{ + ProcessMarkStack(threadId); + return true; +} + +bool SemiSpaceCollector::ParallelHandleThreadRoots(uint32_t threadId) +{ + RootVisitor gcMarkYoung = [this, threadId]([[maybe_unused]] Root type, ObjectSlot slot) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + markObject_.MarkObject(threadId, value.GetTaggedObject(), slot); + } + }; + RootRangeVisitor gcMarkRangeYoung = [this, threadId]([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + markObject_.MarkObject(threadId, value.GetTaggedObject(), slot); + } + } + }; + + rootManager_.VisitVMRoots(gcMarkYoung, gcMarkRangeYoung); + ProcessMarkStack(threadId); + return true; +} + +bool SemiSpaceCollector::ParallelHandleSnapShot(uint32_t threadId) +{ + auto cb = [this, threadId](Region *current) { + auto rememberedSet = current->GetOldToNewRememberedSet(); + if (LIKELY(rememberedSet != nullptr)) { + rememberedSet->IterateOverMarkedChunks([&rememberedSet, this, threadId](void *mem) -> bool { + ObjectSlot slot(ToUintPtr(mem)); + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsWeak()) { + RecordWeakReference(threadId, reinterpret_cast(mem)); + } else if (value.IsHeapObject()) { + auto slotStatus = markObject_.MarkObject(threadId, value.GetTaggedObject(), slot); + if (slotStatus == SlotStatus::CLEAR_SLOT) { + // clear bit + rememberedSet->Clear(ToUintPtr(mem)); + } + } + return true; + }); + } + }; + + heap_->EnumerateSnapShotSpaceRegions(cb); + ProcessMarkStack(threadId); + return true; +} + +void SemiSpaceCollector::ParallelMarkingPhase() +{ + trace::ScopedTrace scoped_trace("SemiSpaceCollector::ParallelMarkingPhase"); + auto oldSpace = heap_->GetOldSpace(); + auto region = oldSpace->GetCurrentRegion(); + + if (paralledGC_) { + heap_->GetThreadPool()->Submit( + std::bind(&SemiSpaceCollector::ParallelHandleThreadRoots, this, std::placeholders::_1)); + heap_->GetThreadPool()->Submit( + std::bind(&SemiSpaceCollector::ParallelHandleSnapShot, this, std::placeholders::_1)); + ParallelHandleOldToNew(0, region); + heap_->GetThreadPool()->WaitTaskFinish(); + } else { + ParallelHandleOldToNew(0, region); + ParallelHandleSnapShot(0); + ParallelHandleThreadRoots(0); + } + + for (uint32_t i = 0; i < heap_->GetThreadPool()->GetThreadNum(); i++) { + SlotNeedUpdate needUpdate(nullptr, ObjectSlot(0)); + while (workList_->GetSlotNeedUpdate(i, &needUpdate)) { + UpdatePromotedSlot(needUpdate.first, needUpdate.second); + } + } +} + +void SemiSpaceCollector::ProcessMarkStack(uint64_t threadId) +{ + while (true) { + TaggedObject *obj = nullptr; + if (!workList_->Pop(threadId, &obj)) { + break; + } + + auto jsHclass = obj->GetClass(); + Region *objectRegion = Region::ObjectAddressToRange(obj); + bool promoted = !objectRegion->InYoungGeneration(); + rootManager_.MarkObjectBody( + obj, jsHclass, + [this, &promoted, threadId]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsWeak()) { + RecordWeakReference(threadId, reinterpret_cast(slot.SlotAddress())); + continue; + } + if (value.IsHeapObject()) { + auto slotStatus = markObject_.MarkObject(threadId, value.GetTaggedObject(), slot); + if (promoted && slotStatus == SlotStatus::KEEP_SLOT) { + SlotNeedUpdate waitUpdate(reinterpret_cast(root), slot); + workList_->PushWaitUpdateSlot(threadId, waitUpdate); + } + } + } + }); + } +} + +void SemiSpaceCollector::SweepPhases() +{ + trace::ScopedTrace scoped_trace("SemiSpaceCollector::SweepPhases"); + for (uint32_t i = 0; i < heap_->GetThreadPool()->GetThreadNum(); i++) { + ProcessQueue *queue = workList_->GetWeakReferenceQueue(i); + while (true) { + auto obj = queue->PopBack(); + if (UNLIKELY(obj == nullptr)) { + break; + } + ObjectSlot slot(ToUintPtr(obj)); + JSTaggedValue value(slot.GetTaggedType()); + auto header = value.GetTaggedWeakRef(); + MarkWord markWord(header); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + auto weakRef = JSTaggedValue(JSTaggedValue(dst).CreateAndGetWeakRef()).GetRawTaggedObject(); + slot.Update(weakRef); + } else { + slot.Update(static_cast(JSTaggedValue::Undefined().GetRawData())); + } + } + } + + auto stringTable = heap_->GetEcmaVM()->GetEcmaStringTable(); + WeakRootVisitor gcUpdateWeak = [](TaggedObject *header) { + Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast(header)); + if (!objectRegion->InYoungGeneration()) { + return header; + } + + MarkWord markWord(header); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + return dst; + } + return reinterpret_cast(ToUintPtr(nullptr)); + }; + stringTable->SweepWeakReference(gcUpdateWeak); + heap_->GetEcmaVM()->ProcessReferences(gcUpdateWeak); +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/semi_space_collector.h b/ecmascript/mem/semi_space_collector.h new file mode 100644 index 0000000000000000000000000000000000000000..fa1c854a1ad40aaf5ddae520d23a3d2704d8984b --- /dev/null +++ b/ecmascript/mem/semi_space_collector.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_H + +#include "ecmascript/mem/clock_scope.h" +#include "ecmascript/mem/mem.h" +#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/allocator.h" +#include "ecmascript/mem/mark_stack-inl.h" +#include "ecmascript/mem/mark_word.h" +#include "ecmascript/mem/slots.h" +#include "ecmascript/mem/heap_roots.h" +#include "ecmascript/mem/remembered_set.h" + +#include "ecmascript/mem/chunk_containers.h" +#include "ecmascript/mem/tlab_allocator.h" + +#include "ecmascript/thread/thread_pool.h" +#include "ecmascript/mem/semi_space_marker.h" + +#include "os/mutex.h" + +namespace panda { +namespace ecmascript { +class Heap; +class JSHClass; +class SemiSpaceWorker; + +class GarbageCollector { +public: + GarbageCollector() = default; + virtual ~GarbageCollector() = default; + DEFAULT_COPY_SEMANTIC(GarbageCollector); + DEFAULT_MOVE_SEMANTIC(GarbageCollector); +}; + +class SemiSpaceCollector : public GarbageCollector { +public: + explicit SemiSpaceCollector(Heap *heap, bool parallelGc); + ~SemiSpaceCollector() override; + NO_COPY_SEMANTIC(SemiSpaceCollector); + NO_MOVE_SEMANTIC(SemiSpaceCollector); + + void RunPhases(); + + Heap *GetHeap() const + { + return heap_; + } + +private: + bool ParallelHandleOldToNew(uint32_t threadId, Region *region); + bool ParallelHandleThreadRoots(uint32_t threadId); + bool ParallelHandleSnapShot(uint32_t threadId); + bool ParallelHandleGlobalPool(uint32_t threadId); + void InitializePhase(); + void ParallelMarkingPhase(); + void SweepPhases(); + void FinishPhase(); + void ProcessMarkStack(uint64_t threadId); + + inline uintptr_t AllocateYoung(size_t size); + inline uintptr_t AllocateOld(size_t size); + inline void UpdatePromotedSlot(TaggedObject *object, ObjectSlot slot); + inline void RecordWeakReference(uint32_t threadId, JSTaggedType *ref); + inline bool BlowAgeMark(uintptr_t address); + + Heap *heap_; + HeapRootManager rootManager_; + os::memory::Mutex allocatorLock_; + BumpPointerAllocator fromSpaceAllocator_{}; + FreeListAllocator oldSpaceAllocator_{}; + bool paralledGC_{false}; + SemiSpaceWorker *workList_{nullptr}; + SemiSpaceMarker markObject_; + size_t promotedSize_{0}; + size_t semiCopiedSize_{0}; + size_t commitSize_ = 0; + uintptr_t ageMark_{0}; + size_t gcTime_{0}; + friend class TlabAllocator; + friend class SemiSpaceWorker; + friend class SemiSpaceMarker; + friend class Heap; +}; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_H diff --git a/ecmascript/mem/semi_space_marker.cpp b/ecmascript/mem/semi_space_marker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bdee1ae4a2d06cb7bdd369c0769781c68110d29a --- /dev/null +++ b/ecmascript/mem/semi_space_marker.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/free_object.h" +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/mem/region.h" +#include "ecmascript/mem/semi_space_collector-inl.h" +#include "ecmascript/mem/semi_space_marker.h" +#include "ecmascript/mem/semi_space_worker.h" +#include "ecmascript/mem/tlab_allocator-inl.h" + +namespace panda::ecmascript { +constexpr int HEAD_SIZE = TaggedObject::ObjectHeaderSize(); + +SemiSpaceMarker::SemiSpaceMarker(SemiSpaceCollector *semiSpaceCollector) : collector_(semiSpaceCollector) {} + +SlotStatus SemiSpaceMarker::MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot) +{ + Region *objectRegion = Region::ObjectAddressToRange(object); + if (!objectRegion->InYoungGeneration()) { + return SlotStatus::CLEAR_SLOT; + } + return MarkYoungGenerationObject(threadId, object, slot); +} + +SlotStatus SemiSpaceMarker::MarkYoungGenerationObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot) +{ + MarkWord markWord(object); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + slot.Update(dst); + Region *valueRegion = Region::ObjectAddressToRange(dst); + return valueRegion->InYoungGeneration() ? SlotStatus::KEEP_SLOT : SlotStatus::CLEAR_SLOT; + } + return EvacuateObject(threadId, object, markWord, slot); +} + +SlotStatus SemiSpaceMarker::EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord, + ObjectSlot slot) +{ + auto klass = markWord.GetJSHClass(); + JSType jsType = klass->GetObjectType(); + auto size = klass->SizeFromJSHClass(jsType, object); + bool isPromoted = ShouldBePromoted(object); + uintptr_t forwardAddress; + if (isPromoted) { + forwardAddress = collector_->workList_->GetTlabAllocator(threadId)->Allocate(size, SpaceAlloc::OLD_SPACE); + if (UNLIKELY(forwardAddress == 0)) { + forwardAddress = collector_->workList_->GetTlabAllocator(threadId)->Allocate(size, SpaceAlloc::YOUNG_SPACE); + if (UNLIKELY(forwardAddress == 0)) { + LOG_ECMA_MEM(FATAL) << "alloc failed"; + UNREACHABLE(); + } + isPromoted = false; + } + } else { + forwardAddress = collector_->workList_->GetTlabAllocator(threadId)->Allocate(size, SpaceAlloc::YOUNG_SPACE); + if (UNLIKELY(forwardAddress == 0)) { + forwardAddress = collector_->workList_->GetTlabAllocator(threadId)->Allocate(size, SpaceAlloc::OLD_SPACE); + if (UNLIKELY(forwardAddress == 0)) { + LOG_ECMA_MEM(FATAL) << "alloc failed"; + UNREACHABLE(); + } + isPromoted = true; + } + } + + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage, hicpp-signed-bitwise) + bool result = Barriers::AtomicSetDynPrimitive(object, 0, markWord.GetValue(), + MarkWord::FromForwardingAddress(forwardAddress)); + if (result) { + CopyObjectWithoutHeader(object, forwardAddress, size); + *reinterpret_cast(forwardAddress) = markWord.GetValue(); + collector_->workList_->AddAliveSize(threadId, size); + if (isPromoted) { + collector_->workList_->AddPromoteSize(threadId, size); + } + collector_->heap_->OnMoveEvent(reinterpret_cast(object), forwardAddress); + if (klass->HasReferenceField(jsType)) { + collector_->workList_->Push(threadId, reinterpret_cast(forwardAddress)); + } + slot.Update(reinterpret_cast(forwardAddress)); + return isPromoted ? SlotStatus::CLEAR_SLOT : SlotStatus::KEEP_SLOT; + } + FreeObject::FillFreeObject(collector_->heap_->GetEcmaVM(), forwardAddress, + AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT))); + auto dst = MarkWord(object).ToForwardingAddress(); + slot.Update(dst); + return Region::ObjectAddressToRange(dst)->InYoungGeneration() ? SlotStatus::KEEP_SLOT : SlotStatus::CLEAR_SLOT; +} + +void SemiSpaceMarker::CopyObjectWithoutHeader(TaggedObject *object, uintptr_t address, size_t size) +{ + if (memcpy_s(ToVoidPtr(address + HEAD_SIZE), size - HEAD_SIZE, ToVoidPtr(ToUintPtr(object) + HEAD_SIZE), + size - HEAD_SIZE) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } +} + +bool SemiSpaceMarker::ShouldBePromoted(TaggedObject *object) +{ + Region *region = Region::ObjectAddressToRange(object); + return (region->BelowAgeMark() || (region->HasAgeMark() && collector_->BlowAgeMark(ToUintPtr(object)))); +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/semi_space_marker.h b/ecmascript/mem/semi_space_marker.h new file mode 100644 index 0000000000000000000000000000000000000000..8aded1fcaf6aeaad1857d50b68344724608c8828 --- /dev/null +++ b/ecmascript/mem/semi_space_marker.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SPACE_MARKER_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SPACE_MARKER_H + +#include "ecmascript/mem/remembered_set.h" +#include "ecmascript/mem/slots.h" +#include "ecmascript/mem/mark_word.h" + +namespace panda::ecmascript { +class JSHClass; +class SemiSpaceCollector; + +class SemiSpaceMarker { +public: + SemiSpaceMarker() = delete; + inline ~SemiSpaceMarker() = default; + NO_COPY_SEMANTIC(SemiSpaceMarker); + NO_MOVE_SEMANTIC(SemiSpaceMarker); + + explicit SemiSpaceMarker(SemiSpaceCollector *semiSpaceCollector); + + SlotStatus MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot); + +private: + SlotStatus MarkYoungGenerationObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot); + SlotStatus EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord, ObjectSlot slot); + bool ShouldBePromoted(TaggedObject *object); + void CopyObjectWithoutHeader(TaggedObject *object, uintptr_t address, size_t size); + + SemiSpaceCollector *collector_; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SPACE_MARKER_H diff --git a/ecmascript/mem/semi_space_worker.cpp b/ecmascript/mem/semi_space_worker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b49115d2d504ad3410c294e8ad16685c40845395 --- /dev/null +++ b/ecmascript/mem/semi_space_worker.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/semi_space_worker.h" + +#include "ecmascript/mem/area.h" +#include "ecmascript/mem/compress_collector.h" +#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/mark_stack.h" +#include "ecmascript/mem/region_factory.h" +#include "ecmascript/mem/tlab_allocator-inl.h" + +namespace panda::ecmascript { +void Worker::Finish(size_t &aliveSize) +{ + for (uint32_t i = 0; i < threadNum_; i++) { + WorkNodeHolder &holder = workList_[i]; + holder.weakQueue_->FinishMarking(continuousQueue_[i]); + delete holder.weakQueue_; + holder.weakQueue_ = nullptr; + delete holder.allocator_; + holder.allocator_ = nullptr; + holder.waitUpdate_.clear(); + aliveSize += holder.aliveSize_; + } + + while (!unuseSpace_.empty()) { + heap_->GetRegionFactory()->FreeBuffer(reinterpret_cast(unuseSpace_.back())); + unuseSpace_.pop_back(); + } +} + +void Worker::Finish(size_t &aliveSize, size_t &promoteSize) +{ + Finish(aliveSize); + for (uint32_t i = 0; i < threadNum_; i++) { + WorkNodeHolder &holder = workList_[i]; + promoteSize += holder.aliveSize_; + } +} + +bool Worker::Push(uint32_t threadId, TaggedObject *object) +{ + WorkNode *&pushNode = workList_[threadId].pushNode_; + if (!pushNode->Push(ToUintPtr(object))) { + PushWorkNodeToGlobal(threadId); + return pushNode->Push(ToUintPtr(object)); + } + return true; +} + +bool Worker::Pop(uint32_t threadId, TaggedObject **object) +{ + WorkNode *&popNode = workList_[threadId].popNode_; + WorkNode *&pushNode = workList_[threadId].pushNode_; + if (!popNode->Pop(reinterpret_cast(object))) { + if (!pushNode->IsEmpty()) { + WorkNode *tmp = popNode; + popNode = pushNode; + pushNode = tmp; + } else if (!PopWorkNodeFromGlobal(threadId)) { + return false; + } + return popNode->Pop(reinterpret_cast(object)); + } + return true; +} + +bool Worker::PopWorkNodeFromGlobal(uint32_t threadId) +{ + return globalWork_.Pop(&workList_[threadId].popNode_); +} + +WorkNode *Worker::AllocalWorkNode() +{ + size_t totalSize = sizeof(WorkNode) + sizeof(Stack) + STACK_AREA_SIZE; + // CAS + volatile auto atomicField = reinterpret_cast *>(&spaceTop_); + bool result = false; + uintptr_t begin = 0; + do { + begin = atomicField->load(std::memory_order_acquire); + if (begin + totalSize >= markSpaceEnd_) { + os::memory::LockHolder lock(mtx_); + begin = atomicField->load(std::memory_order_acquire); + if (begin + totalSize >= markSpaceEnd_) { + unuseSpace_.emplace_back(markSpace_); + markSpace_ = ToUintPtr(heap_->GetRegionFactory()->AllocateBuffer(SPACE_SIZE)); + spaceTop_ = markSpace_; + markSpaceEnd_ = markSpace_ + SPACE_SIZE; + begin = spaceTop_; + } + } + result = std::atomic_compare_exchange_strong_explicit(atomicField, &begin, begin + totalSize, + std::memory_order_release, std::memory_order_relaxed); + } while (!result); + Stack *stack = reinterpret_cast(begin + sizeof(WorkNode)); + stack->ResetBegin(begin + sizeof(WorkNode) + sizeof(Stack), begin + totalSize); + WorkNode *work = reinterpret_cast(begin); + return new (work) WorkNode(stack); +} + +Worker::Worker(Heap *heap, uint32_t threadNum) + : heap_(heap), threadNum_(threadNum), markSpace_(0), spaceTop_(0), markSpaceEnd_(0) +{ + for (uint32_t i = 0; i < threadNum_; i++) { + continuousQueue_[i] = new ProcessQueue(heap); + } + markSpace_ = ToUintPtr(heap_->GetRegionFactory()->AllocateBuffer(SPACE_SIZE)); +} + +Worker::~Worker() +{ + for (uint32_t i = 0; i < threadNum_; i++) { + continuousQueue_[i]->TearDown(); + delete continuousQueue_[i]; + continuousQueue_[i] = nullptr; + } + heap_->GetRegionFactory()->FreeBuffer(reinterpret_cast(markSpace_)); +} + +SemiSpaceWorker::~SemiSpaceWorker() = default; + +CompressGCWorker::~CompressGCWorker() = default; + +void SemiSpaceWorker::PushWorkNodeToGlobal(uint32_t threadId) +{ + WorkNode *&pushNode = workList_[threadId].pushNode_; + if (!pushNode->IsEmpty()) { + globalWork_.Push(pushNode); + pushNode = AllocalWorkNode(); + + auto pool = heap_->GetThreadPool(); + if (pool->GetTaskCount() < pool->GetThreadNum() - 1) { + pool->Submit(std::bind(&SemiSpaceCollector::ParallelHandleGlobalPool, heap_->GetSemiSpaceCollector(), + std::placeholders::_1)); + } + } +} + +void SemiSpaceWorker::Initialize() +{ + spaceTop_ = markSpace_; + markSpaceEnd_ = markSpace_ + SPACE_SIZE; + for (uint32_t i = 0; i < threadNum_; i++) { + WorkNodeHolder &holder = workList_[i]; + holder.pushNode_ = AllocalWorkNode(); + holder.popNode_ = AllocalWorkNode(); + holder.weakQueue_ = new ProcessQueue(); + holder.weakQueue_->BeginMarking(heap_, continuousQueue_[i]); + holder.allocator_ = new TlabAllocator(heap_, TriggerGCType::SEMI_GC); + holder.aliveSize_ = 0; + holder.promoteSize_ = 0; + } +} + +void CompressGCWorker::PushWorkNodeToGlobal(uint32_t threadId) +{ + WorkNode *&pushNode = workList_[threadId].pushNode_; + if (!pushNode->IsEmpty()) { + globalWork_.Push(pushNode); + pushNode = AllocalWorkNode(); + + auto pool = heap_->GetThreadPool(); + if (pool->GetTaskCount() < pool->GetThreadNum() - 1) { + pool->Submit( + std::bind(&CompressCollector::ProcessMarkStack, heap_->GetCompressCollector(), std::placeholders::_1)); + } + } +} + +void CompressGCWorker::Initialize() +{ + spaceTop_ = markSpace_; + markSpaceEnd_ = markSpace_ + SPACE_SIZE; + for (uint32_t i = 0; i < threadNum_; i++) { + WorkNodeHolder &holder = workList_[i]; + holder.pushNode_ = AllocalWorkNode(); + holder.popNode_ = AllocalWorkNode(); + holder.weakQueue_ = new ProcessQueue(); + holder.weakQueue_->BeginMarking(heap_, continuousQueue_[i]); + holder.allocator_ = new TlabAllocator(heap_, TriggerGCType::COMPRESS_FULL_GC); + holder.aliveSize_ = 0; + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/semi_space_worker.h b/ecmascript/mem/semi_space_worker.h new file mode 100644 index 0000000000000000000000000000000000000000..e04e0e0d669c622b21d755afb5db2ec96082f666 --- /dev/null +++ b/ecmascript/mem/semi_space_worker.h @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SPACE_WORKER_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SPACE_WORKER_H + +#include "ecmascript/mem/mark_stack.h" +#include "ecmascript/mem/slots.h" + +namespace panda::ecmascript { +using SlotNeedUpdate = std::pair; + +static constexpr uint32_t MARKSTACK_MAX_SIZE = 100; +static constexpr uint32_t THREAD_NUM_FOR_YOUNG_GC = 6; +static constexpr uint32_t STACK_AREA_SIZE = sizeof(uintptr_t *) * MARKSTACK_MAX_SIZE; + +static constexpr uint32_t SPACE_SIZE = 8 * 1024; + +class Heap; +class Stack; +class SemiSpaceCollector; +class TlabAllocator; + +class WorkNode { +public: + explicit WorkNode(Stack *stack) : next_(nullptr), stack_(stack) {} + ~WorkNode() + { + delete stack_; + stack_ = nullptr; + } + + NO_COPY_SEMANTIC(WorkNode); + NO_MOVE_SEMANTIC(WorkNode); + + bool Push(uintptr_t obj) + { + return stack_->PushBackChecked(obj); + } + + bool Pop(uintptr_t *obj) + { + if (IsEmpty()) { + return false; + } + auto object = ToVoidPtr(*obj); + if (object != nullptr) { + delete reinterpret_cast(object); + } + *obj = stack_->PopBackUnchecked(); + return true; + } + + bool IsEmpty() const + { + return stack_->IsEmpty(); + } + + WorkNode *Next() const + { + return next_; + } + + void SetNext(WorkNode *node) + { + next_ = node; + } + +private: + WorkNode *next_; + Stack *stack_; +}; + +class GlobalWorkList { +public: + GlobalWorkList() : top_(nullptr) {} + ~GlobalWorkList() = default; + + NO_COPY_SEMANTIC(GlobalWorkList); + NO_MOVE_SEMANTIC(GlobalWorkList); + + void Push(WorkNode *node) + { + if (node == nullptr) { + return; + } + os::memory::LockHolder lock(mtx_); + node->SetNext(top_); + top_ = node; + } + + bool Pop(WorkNode **node) + { + os::memory::LockHolder lock(mtx_); + if (top_ == nullptr) { + return false; + } + *node = top_; + top_ = top_->Next(); + return true; + } + +private: + WorkNode *top_; + os::memory::Mutex mtx_; +}; + +struct WorkNodeHolder { + WorkNode *pushNode_{nullptr}; + WorkNode *popNode_{nullptr}; + ProcessQueue *weakQueue_{nullptr}; + std::vector waitUpdate_; + TlabAllocator *allocator_{nullptr}; + size_t aliveSize_ = 0; + size_t promoteSize_ = 0; +}; + +class Worker { +public: + Worker() = delete; + explicit Worker(Heap *heap, uint32_t threadNum); + + virtual ~Worker() = 0; + virtual void PushWorkNodeToGlobal(uint32_t threadId) = 0; + virtual void Initialize() = 0; + + void Finish(size_t &aliveSize); + void Finish(size_t &aliveSize, size_t &promoteSize); + + bool Push(uint32_t threadId, TaggedObject *object); + bool Pop(uint32_t threadId, TaggedObject **object); + + bool PopWorkNodeFromGlobal(uint32_t threadId); + + void PushWeakReference(uint32_t threadId, JSTaggedType *weak) + { + workList_[threadId].weakQueue_->PushBack(weak); + } + + void AddAliveSize(uint32_t threadId, size_t size) + { + workList_[threadId].aliveSize_ += size; + } + + void AddPromoteSize(uint32_t threadId, size_t size) + { + workList_[threadId].promoteSize_ += size; + } + + ProcessQueue *GetWeakReferenceQueue(uint32_t threadId) const + { + return workList_[threadId].weakQueue_; + } + + TlabAllocator *GetTlabAllocator(uint32_t threadId) const + { + return workList_[threadId].allocator_; + } + + NO_COPY_SEMANTIC(Worker); + NO_MOVE_SEMANTIC(Worker); + +protected: + WorkNode *AllocalWorkNode(); + + Heap *heap_; // NOLINT(misc-non-private-member-variables-in-classes) + uint32_t threadNum_; // NOLINT(misc-non-private-member-variables-in-classes) + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes, modernize-avoid-c-arrays) + WorkNodeHolder workList_[THREAD_NUM_FOR_YOUNG_GC]; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes, modernize-avoid-c-arrays) + ContinuousStack *continuousQueue_[THREAD_NUM_FOR_YOUNG_GC]; + GlobalWorkList globalWork_; // NOLINT(misc-non-private-member-variables-in-classes) + + uintptr_t markSpace_; // NOLINT(misc-non-private-member-variables-in-classes) + uintptr_t spaceTop_; // NOLINT(misc-non-private-member-variables-in-classes) + uintptr_t markSpaceEnd_; // NOLINT(misc-non-private-member-variables-in-classes) + +private: + std::vector unuseSpace_; + os::memory::Mutex mtx_; +}; + +class SemiSpaceWorker : public Worker { +public: + SemiSpaceWorker() = delete; + explicit SemiSpaceWorker(Heap *heap, uint32_t threadNum) : Worker(heap, threadNum) {} + + ~SemiSpaceWorker() override; + void PushWorkNodeToGlobal(uint32_t threadId) override; + void Initialize() override; + + inline void PushWaitUpdateSlot(uint32_t threadId, SlotNeedUpdate slot) + { + workList_[threadId].waitUpdate_.emplace_back(slot); + } + + inline bool GetSlotNeedUpdate(uint32_t threadId, SlotNeedUpdate *slot) + { + std::vector &waitUpdate = workList_[threadId].waitUpdate_; + if (waitUpdate.empty()) { + return false; + } + *slot = waitUpdate.back(); + waitUpdate.pop_back(); + return true; + } + + NO_COPY_SEMANTIC(SemiSpaceWorker); + NO_MOVE_SEMANTIC(SemiSpaceWorker); +}; + +class CompressGCWorker : public Worker { +public: + CompressGCWorker() = delete; + explicit CompressGCWorker(Heap *heap, uint32_t threadNum) : Worker(heap, threadNum) {} + + ~CompressGCWorker() override; + void PushWorkNodeToGlobal(uint32_t threadId) override; + void Initialize() override; + + NO_COPY_SEMANTIC(CompressGCWorker); + NO_MOVE_SEMANTIC(CompressGCWorker); +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_SEMI_SPACE_WORKER_H diff --git a/ecmascript/mem/slots.h b/ecmascript/mem/slots.h new file mode 100644 index 0000000000000000000000000000000000000000..607c13c3fb9bcbdf453799dbafd0b16c4a449d16 --- /dev/null +++ b/ecmascript/mem/slots.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_SLOTS_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_SLOTS_H + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/mem/mem.h" + +namespace panda::ecmascript { +class ObjectSlot { +public: + explicit ObjectSlot(uintptr_t slotAddr) : slotAddress_(slotAddr) {} + ~ObjectSlot() = default; + + DEFAULT_COPY_SEMANTIC(ObjectSlot); + DEFAULT_MOVE_SEMANTIC(ObjectSlot); + + void Update(TaggedObject *header) + { + Update(static_cast(ToUintPtr(header))); + } + + void Update(JSTaggedType value) + { + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) + *reinterpret_cast(slotAddress_) = value; + } + + TaggedObject *GetTaggedObjectHeader() const + { + return reinterpret_cast(GetTaggedType()); + } + + JSTaggedType GetTaggedType() const + { + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) + return *reinterpret_cast(slotAddress_); + } + + ObjectSlot &operator++() + { + slotAddress_ += sizeof(JSTaggedType); + return *this; + } + + // NOLINTNEXTLINE(cert-dcl21-cpp) + ObjectSlot operator++(int) + { + ObjectSlot ret = *this; + slotAddress_ += sizeof(JSTaggedType); + return ret; + } + + uintptr_t SlotAddress() const + { + return slotAddress_; + } + + bool operator<(const ObjectSlot &other) const + { + return slotAddress_ < other.slotAddress_; + } + bool operator<=(const ObjectSlot &other) const + { + return slotAddress_ <= other.slotAddress_; + } + bool operator>(const ObjectSlot &other) const + { + return slotAddress_ > other.slotAddress_; + } + bool operator>=(const ObjectSlot &other) const + { + return slotAddress_ >= other.slotAddress_; + } + bool operator==(const ObjectSlot &other) const + { + return slotAddress_ == other.slotAddress_; + } + bool operator!=(const ObjectSlot &other) const + { + return slotAddress_ != other.slotAddress_; + } + +private: + uintptr_t slotAddress_; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_SLOTS_H diff --git a/ecmascript/mem/space-inl.h b/ecmascript/mem/space-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..c9369710d336304912196be3c5770e82cb606df6 --- /dev/null +++ b/ecmascript/mem/space-inl.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_SPACE_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_SPACE_INL_H + +#include "ecmascript/mem/space.h" +#include "ecmascript/mem/remembered_set.h" + +namespace panda::ecmascript { +template +void Space::EnumerateRegions(const Callback &cb, Region *region) const +{ + Region *current = regionList_.GetFirst(); + if (region == nullptr) { + region = regionList_.GetLast(); + } + while (current != region) { + auto next = current->GetNext(); + cb(current); + current = next; + } + + if (region != nullptr) { + cb(current); + } +} + +void Region::InsertCrossRegionRememberedSet(uintptr_t addr) +{ + auto set = GetOrCreateCrossRegionRememberedSet(); + set->Insert(addr); +} + +void Region::InsertOldToNewRememberedSet(uintptr_t addr) +{ + auto set = GetOrCreateOldToNewRememberedSet(); + set->Insert(addr); +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_SPACE_INL_H diff --git a/ecmascript/mem/space.cpp b/ecmascript/mem/space.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d499296c2f70d75b49237d05601a8ad8865dd64d --- /dev/null +++ b/ecmascript/mem/space.cpp @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/class_linker/program_object.h" +#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/mem_controller.h" +#include "ecmascript/mem/region_factory.h" +#include "ecmascript/mem/remembered_set.h" +#include "ecmascript/mem/space-inl.h" +#include "libpandabase/utils/logger.h" + +namespace panda::ecmascript { +void Space::AddRegion(Region *region) +{ + regionList_.AddNode(region); + IncrementCommitted(region->GetCapacity()); +} + +void Space::SetUp() +{ + Region *region = heap_->GetRegionFactory()->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); + if (spaceType_ == MemSpaceType::SEMI_SPACE) { + region->SetFlag(RegionFlags::IS_IN_YOUNG_GENERATION); + } else if (spaceType_ == MemSpaceType::SNAPSHOT_SPACE) { + region->SetFlag(RegionFlags::IS_IN_SNAPSHOT_GENERATION); + } else if (spaceType_ == MemSpaceType::OLD_SPACE) { + region->SetFlag(RegionFlags::IS_IN_OLD_GENERATION); + } + AddRegion(region); +} + +void Space::ReclaimRegions() +{ + EnumerateRegions([this](Region *current) { ClearAndFreeRegion(current); }); + regionList_.Clear(); + committedSize_ = 0; +} + +void Space::ClearAndFreeRegion(Region *region) +{ + if (region->GetMarkBitmap() != nullptr) { + auto bitmap = region->GetMarkBitmap(); + auto size = RangeBitmap::GetBitMapSizeInByte(region->GetCapacity()); + heap_->GetRegionFactory()->Free(bitmap->GetBitMap().Data(), size); + delete bitmap; + } + if (region->GetCrossRegionRememberedSet() != nullptr) { + auto rememberedSet = region->GetCrossRegionRememberedSet(); + auto size = RememberedSet::GetSizeInByte(region->GetCapacity()); + heap_->GetRegionFactory()->Free(rememberedSet->GetBitMap().Data(), size); + delete rememberedSet; + } + if (region->GetOldToNewRememberedSet() != nullptr) { + auto rememberedSet = region->GetOldToNewRememberedSet(); + auto size = RememberedSet::GetSizeInByte(region->GetCapacity()); + heap_->GetRegionFactory()->Free(rememberedSet->GetBitMap().Data(), size); + delete rememberedSet; + } + DecrementCommitted(region->GetCapacity()); + heap_->GetRegionFactory()->FreeRegion(region); +} + +size_t Space::GetHeapObjectSize() const +{ + Region *last = GetCurrentRegion(); + auto top = GetHeap()->GetHeapManager()->GetNewSpaceAllocator().GetTop(); + size_t result = last->GetAllocatedBytes(top); + + EnumerateRegions([&result, &last](Region *current) { + if (current != last) { + result += current->GetAllocatedBytes(); + } + }); + + return result; +} + +SemiSpace::SemiSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) + : Space(heap, MemSpaceType::SEMI_SPACE, initialCapacity, maximumCapacity), ageMark_(0) +{ +} + +bool SemiSpace::Expand(uintptr_t top) +{ + Heap *heap = GetHeap(); + if (GetCommittedSize() >= GetMaximumCapacity()) { + return false; + } + GetCurrentRegion()->SetHighWaterMark(top); + Region *region = heap->GetRegionFactory()->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); + region->SetFlag(RegionFlags::IS_IN_YOUNG_GENERATION); + + AddRegion(region); + std::atomic_thread_fence(std::memory_order_seq_cst); + return true; +} + +void SemiSpace::Swap([[maybe_unused]] SemiSpace *other) {} + +bool SemiSpace::ContainObject(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + return true; + } + region = region->GetNext(); + } + + return false; +} + +bool SemiSpace::IsLive(TaggedObject *object) const +{ + return ContainObject(object); +} + +void SemiSpace::IterateOverObjects(const std::function &visitor) const +{ + auto current = GetCurrentRegion(); + EnumerateRegions([&](Region *region) { + auto curPtr = region->GetBegin(); + uintptr_t endPtr; + if (region == current) { + auto top = GetHeap()->GetHeapManager()->GetNewSpaceAllocator().GetTop(); + endPtr = curPtr + region->GetAllocatedBytes(top); + } else { + endPtr = curPtr + region->GetAllocatedBytes(); + } + + while (curPtr < endPtr) { + auto freeObject = FreeObject::Cast(curPtr); + size_t objSize; + if (!freeObject->IsFreeObject()) { + auto obj = reinterpret_cast(curPtr); + visitor(obj); + objSize = obj->GetObjectSize(); + } else { + objSize = freeObject->Available(); + } + LOG_IF(objSize == 0, FATAL, RUNTIME) << "SemiSpace IterateOverObjects objSize==0 invalid: " << curPtr; + curPtr += AlignUp(objSize, sizeof(JSTaggedType)); + } + }); +} + +OldSpace::OldSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) + : Space(heap, MemSpaceType::OLD_SPACE, initialCapacity, maximumCapacity) +{ +} + +bool OldSpace::Expand() +{ + if (GetCommittedSize() >= GetMaximumCapacity()) { + LOG_ECMA_MEM(FATAL) << "Committed size " << GetCommittedSize() << " of old space is too big. "; + return false; + } + Region *region = GetHeap()->GetRegionFactory()->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); + region->SetFlag(RegionFlags::IS_IN_OLD_GENERATION); + AddRegion(region); + return true; +} + +bool OldSpace::ContainObject(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + return true; + } + region = region->GetNext(); + } + return false; +} + +bool OldSpace::IsLive(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + auto freeObject = FreeObject::Cast(ToUintPtr(object)); + return !freeObject->IsFreeObject(); + } + region = region->GetNext(); + } + return false; +} + +void OldSpace::IterateOverObjects(const std::function &visitor) const +{ + EnumerateRegions([&](Region *region) { + uintptr_t curPtr = region->GetBegin(); + uintptr_t endPtr = region->GetEnd(); + while (curPtr < endPtr) { + auto freeObject = FreeObject::Cast(curPtr); + size_t objSize; + if (!freeObject->IsFreeObject()) { + auto obj = reinterpret_cast(curPtr); + visitor(obj); + objSize = obj->GetObjectSize(); + } else { + objSize = freeObject->Available(); + } + LOG_IF(objSize == 0, FATAL, RUNTIME) << "OldSpace IterateOverObjects objSize==0 invalid: " << curPtr; + curPtr += AlignUp(objSize, sizeof(JSTaggedType)); + } + }); +} + +size_t OldSpace::GetHeapObjectSize() const +{ + size_t result; + size_t availableSize = GetHeap()->GetHeapManager()->GetOldSpaceAllocator().GetAvailableSize(); + size_t regionSize = GetRegionList().GetLength() * DEFAULT_REGION_SIZE; + result = regionSize - availableSize; + result += GetHeap()->GetLargeObjectSpace()->GetHeapObjectSize(); + return result; +} + +NonMovableSpace::NonMovableSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) + : Space(heap, MemSpaceType::NON_MOVABLE, initialCapacity, maximumCapacity) +{ +} + +bool NonMovableSpace::Expand() +{ + if (GetCommittedSize() >= GetMaximumCapacity()) { + LOG_ECMA_MEM(FATAL) << "Committed size " << GetCommittedSize() << " of non movable space is too big. "; + return false; + } + Region *region = GetHeap()->GetRegionFactory()->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); + AddRegion(region); + return true; +} + +size_t NonMovableSpace::GetHeapObjectSize() const +{ + size_t result; + size_t availableSize = GetHeap()->GetHeapManager()->GetNonMovableSpaceAllocator().GetAvailableSize(); + size_t regionSize = GetRegionList().GetLength() * DEFAULT_REGION_SIZE; + result = regionSize - availableSize; + return result; +} + +SnapShotSpace::SnapShotSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) + : Space(heap, MemSpaceType::SNAPSHOT_SPACE, initialCapacity, maximumCapacity) +{ +} + +bool SnapShotSpace::Expand(uintptr_t top) +{ + if (GetCommittedSize() >= GetMaximumCapacity()) { + return false; + } + Region *current = GetCurrentRegion(); + if (current != nullptr) { + current->SetHighWaterMark(top); + } + Region *region = GetHeap()->GetRegionFactory()->AllocateAlignedRegion(this, DEFAULT_SNAPSHOT_SPACE_SIZE); + region->SetFlag(RegionFlags::IS_IN_SNAPSHOT_GENERATION); + AddRegion(region); + return true; +} + +RangeBitmap *Region::CreateMarkBitmap() +{ + size_t heapSize = IsFlagSet(RegionFlags::IS_LARGE_OBJECT) ? LARGE_BITMAP_MIN_SIZE : GetCapacity(); + // Only one large object is stored in a region. The BitmapSize of a large region will always be 8 Bytes. + size_t bitmapSize = RangeBitmap::GetBitMapSizeInByte(heapSize); + + auto bitmapData = space_->GetHeap()->GetRegionFactory()->Allocate(bitmapSize); + auto *ret = new RangeBitmap(this, heapSize, bitmapData); + + ret->ClearAllBits(); + return ret; +} + +RememberedSet *Region::CreateRememberedSet() +{ + auto setSize = RememberedSet::GetSizeInByte(GetCapacity()); + auto setAddr = space_->GetHeap()->GetRegionFactory()->Allocate(setSize); + uintptr_t setData = ToUintPtr(setAddr); + auto ret = new RememberedSet(ToUintPtr(this), GetCapacity(), setData); + ret->ClearAllBits(); + return ret; +} + +void Space::TearDown() +{ + ReclaimRegions(); +} + +void NonMovableSpace::IterateOverObjects(const std::function &visitor) const +{ + EnumerateRegions([&](Region *region) { + uintptr_t curPtr = region->GetBegin(); + uintptr_t endPtr = region->GetEnd(); + while (curPtr < endPtr) { + auto freeObject = FreeObject::Cast(curPtr); + size_t objSize; + if (!freeObject->IsFreeObject()) { + auto obj = reinterpret_cast(curPtr); + visitor(obj); + objSize = obj->GetObjectSize(); + } else { + objSize = freeObject->Available(); + } + LOG_IF(objSize == 0, FATAL, RUNTIME) << "NonMovableSpace IterateOverObjects objSize==0 invalid: " << curPtr; + curPtr += AlignUp(objSize, sizeof(JSTaggedType)); + } + }); +} + +bool NonMovableSpace::ContainObject(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + return true; + } + region = region->GetNext(); + } + return false; +} + +bool NonMovableSpace::IsLive(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + auto freeObject = FreeObject::Cast(ToUintPtr(object)); + return !freeObject->IsFreeObject(); + } + region = region->GetNext(); + } + return false; +} + +LargeObjectSpace::LargeObjectSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) + : Space(heap, MemSpaceType::LARGE_OBJECT_SPACE, initialCapacity, maximumCapacity) +{ +} + +uintptr_t LargeObjectSpace::Allocate(size_t objectSize) +{ + if (GetCommittedSize() >= GetMaximumCapacity()) { + LOG_ECMA_MEM(FATAL) << "Committed size " << GetCommittedSize() << " of large object space is too big. " + << "length: " << GetRegionList().GetLength(); + return 0; + } + size_t alignedSize = AlignUp(objectSize + sizeof(Region), PANDA_POOL_ALIGNMENT_IN_BYTES); + if (UNLIKELY(alignedSize > MAX_LARGE_OBJECT_SIZE)) { + LOG_ECMA_MEM(FATAL) << "The size is too big for this allocator. Return nullptr."; + return 0; + } + Region *region = GetHeap()->GetRegionFactory()->AllocateAlignedRegion(this, alignedSize); + region->SetFlag(RegionFlags::IS_LARGE_OBJECT); + AddRegion(region); + return region->GetBegin(); +} + +bool LargeObjectSpace::ContainObject(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + return true; + } + region = region->GetNext(); + } + return false; +} + +bool LargeObjectSpace::IsLive(TaggedObject *object) const +{ + return ContainObject(object); +} + +size_t LargeObjectSpace::GetHeapObjectSize() const +{ + size_t result = 0; + EnumerateRegions([&result](Region *current) { + auto obj = reinterpret_cast(current->GetBegin()); + result += obj->GetObjectSize(); + }); + return result; +} + +void LargeObjectSpace::IterateOverObjects(const std::function &objectVisitor) const +{ + EnumerateRegions([&](Region *region) { + uintptr_t curPtr = region->GetBegin(); + objectVisitor(reinterpret_cast(curPtr)); + }); +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/space.h b/ecmascript/mem/space.h new file mode 100644 index 0000000000000000000000000000000000000000..16a91acad7edbb14a234b877149d7547bd74e7ce --- /dev/null +++ b/ecmascript/mem/space.h @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_SPACE_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_SPACE_H + +#include "mem/gc/bitmap.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/mem/ecma_list.h" +#include "ecmascript/mem/mem.h" +#include "ecmascript/mem/region.h" +#include "libpandabase/utils/type_helpers.h" +#include "libpandafile/file.h" + +namespace panda::ecmascript { +class Heap; +class Program; + +enum MemSpaceType { + SEMI_SPACE, + OLD_SPACE, + NON_MOVABLE, + LARGE_OBJECT_SPACE, + SNAPSHOT_SPACE, + SPACE_TYPE_LAST // Count of different types +}; + +enum TriggerGCType { + SEMI_GC, + OLD_GC, + NON_MOVE_GC, + LARGE_GC, + COMPRESS_FULL_GC, + GC_TYPE_LAST // Count of different types +}; + +constexpr MemSpaceType ToSpaceType(size_t index) +{ + return static_cast(index); +} + +static constexpr size_t SPACE_TYPE_SIZE = helpers::ToUnderlying(MemSpaceType::SPACE_TYPE_LAST); + +class Space { +public: + Space(Heap *heap, MemSpaceType spaceType, size_t initialCapacity, size_t maximumCapacity) + : heap_(heap), + spaceType_(spaceType), + initialCapacity_(initialCapacity), + maximumCapacity_(maximumCapacity), + committedSize_(0) + { + } + virtual ~Space() = default; + NO_COPY_SEMANTIC(Space); + NO_MOVE_SEMANTIC(Space); + + Heap *GetHeap() const + { + return heap_; + } + + size_t GetMaximumCapacity() const + { + return maximumCapacity_; + } + + void SetMaximumCapacity(size_t maximumCapacity) + { + maximumCapacity_ = maximumCapacity; + } + + size_t GetInitialCapacity() const + { + return initialCapacity_; + } + + size_t GetCommittedSize() const + { + return committedSize_; + } + + void IncrementCommitted(size_t bytes) + { + committedSize_ += bytes; + } + + void DecrementCommitted(size_t bytes) + { + committedSize_ -= bytes; + } + + MemSpaceType GetSpaceType() const + { + return spaceType_; + } + + uintptr_t GetAllocateAreaBegin() const + { + return regionList_.GetLast()->GetBegin(); + } + + uintptr_t GetAllocateAreaEnd() const + { + return regionList_.GetLast()->GetEnd(); + } + + Region *GetCurrentRegion() const + { + return regionList_.GetLast(); + } + + uint32_t GetRegionCount() + { + return regionList_.GetLength(); + } + + EcmaList &GetRegionList() + { + return regionList_; + } + + const EcmaList &GetRegionList() const + { + return regionList_; + } + + size_t GetHeapObjectSize() const; + + template + void EnumerateRegions(const Callback &cb, Region *region = nullptr) const; + + void AddRegion(Region *region); + + void SetUp(); + void TearDown(); + + void ReclaimRegions(); + + void ClearAndFreeRegion(Region *region); + +private: + Heap *heap_; + EcmaList regionList_; + MemSpaceType spaceType_; + size_t initialCapacity_; + size_t maximumCapacity_; + size_t committedSize_; +}; + +class SemiSpace : public Space { +public: + explicit SemiSpace(Heap *heap, size_t initialCapacity = DEFAULT_SEMI_SPACE_SIZE, + size_t maximumCapacity = SEMI_SPACE_SIZE_4M); + ~SemiSpace() override = default; + NO_COPY_SEMANTIC(SemiSpace); + NO_MOVE_SEMANTIC(SemiSpace); + + void SetAgeMark(uintptr_t mark) + { + ageMark_ = mark; + } + + uintptr_t GetAgeMark() const + { + return ageMark_; + } + + bool Expand(uintptr_t top); + + void Swap(SemiSpace *other); + + bool ContainObject(TaggedObject *object) const; + bool IsLive(TaggedObject *object) const; + void IterateOverObjects(const std::function &objectVisitor) const; + +private: + uintptr_t ageMark_; +}; + +class OldSpace : public Space { +public: + explicit OldSpace(Heap *heap, size_t initialCapacity = DEFAULT_OLD_SPACE_SIZE, + size_t maximumCapacity = MAX_OLD_SPACE_SIZE); + ~OldSpace() override = default; + NO_COPY_SEMANTIC(OldSpace); + NO_MOVE_SEMANTIC(OldSpace); + bool Expand(); + bool ContainObject(TaggedObject *object) const; + bool IsLive(TaggedObject *object) const; + void IterateOverObjects(const std::function &objectVisitor) const; + size_t GetHeapObjectSize() const; +}; + +class NonMovableSpace : public Space { +public: + explicit NonMovableSpace(Heap *heap, size_t initialCapacity = DEFAULT_NON_MOVABLE_SPACE_SIZE, + size_t maximumCapacity = MAX_NON_MOVABLE_SPACE_SIZE); + ~NonMovableSpace() override = default; + NO_COPY_SEMANTIC(NonMovableSpace); + NO_MOVE_SEMANTIC(NonMovableSpace); + bool Expand(); + bool ContainObject(TaggedObject *object) const; + bool IsLive(TaggedObject *object) const; + void IterateOverObjects(const std::function &objectVisitor) const; + size_t GetHeapObjectSize() const; +}; + +class SnapShotSpace : public Space { +public: + explicit SnapShotSpace(Heap *heap, size_t initialCapacity = DEFAULT_SNAPSHOT_SPACE_SIZE, + size_t maximumCapacity = MAX_SNAPSHOT_SPACE_SIZE); + ~SnapShotSpace() override = default; + NO_COPY_SEMANTIC(SnapShotSpace); + NO_MOVE_SEMANTIC(SnapShotSpace); + bool Expand(uintptr_t top); +}; + +class LargeObjectSpace : public Space { +public: + explicit LargeObjectSpace(Heap *heap, size_t initialCapacity = DEFAULT_OLD_SPACE_SIZE, + size_t maximumCapacity = MAX_LARGE_OBJECT_SPACE_SIZE); + ~LargeObjectSpace() override = default; + NO_COPY_SEMANTIC(LargeObjectSpace); + NO_MOVE_SEMANTIC(LargeObjectSpace); + uintptr_t Allocate(size_t objectSize); + size_t GetHeapObjectSize() const; + bool ContainObject(TaggedObject *object) const; + bool IsLive(TaggedObject *object) const; + void IterateOverObjects(const std::function &objectVisitor) const; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_SPACE_H diff --git a/ecmascript/mem/tagged_object-inl.h b/ecmascript/mem/tagged_object-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..f935a062cd779e2299699c606473eac6379a1401 --- /dev/null +++ b/ecmascript/mem/tagged_object-inl.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TAGGED_OBJECT_HEADER_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_TAGGED_OBJECT_HEADER_INL_H + +#include "ecmascript/mem/tagged_object.h" + +#include +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_handle.h" + +namespace panda::ecmascript { +inline void TaggedObject::SetClass(JSHClass *hclass) +{ + *reinterpret_cast(ToUintPtr(this)) = reinterpret_cast(hclass); +} + +inline void TaggedObject::SetClass(JSHandle hclass) +{ + SetClass(*hclass); +} + +inline JSHClass *TaggedObject::GetClass() const +{ + return reinterpret_cast(*reinterpret_cast(ToUintPtr(this))); +} + +inline void TaggedObject::SynchronizedSetClass(JSHClass *hclass) +{ + reinterpret_cast *>(this)->store(reinterpret_cast(hclass), + std::memory_order_release); +} + +inline JSHClass *TaggedObject::SynchronizedGetClass() const +{ + return reinterpret_cast( + reinterpret_cast *>(ToUintPtr(this))->load(std::memory_order_acquire)); +} +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_TAGGED_OBJECT_HEADER_INL_H diff --git a/ecmascript/mem/tagged_object.cpp b/ecmascript/mem/tagged_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..49d82618fa30e83c3bc61b6918fe07693006548e --- /dev/null +++ b/ecmascript/mem/tagged_object.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/tagged_object.h" + +#include "ecmascript/js_hclass-inl.h" + +namespace panda::ecmascript { +size_t TaggedObject::GetObjectSize() +{ + JSHClass *cls = GetClass(); + return cls->SizeFromJSHClass(cls->GetObjectType(), this); +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/tagged_object.h b/ecmascript/mem/tagged_object.h new file mode 100644 index 0000000000000000000000000000000000000000..1f5d37de442fe2f0f434c5bb2fd402b7b3253a7a --- /dev/null +++ b/ecmascript/mem/tagged_object.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TAGGED_OBJECT_HEADER_H +#define PANDA_RUNTIME_ECMASCRIPT_TAGGED_OBJECT_HEADER_H + +#include "ecmascript/mem/mark_word.h" +#include "include/object_header.h" + +namespace panda::ecmascript { +class JSHClass; +template +class JSHandle; + +class TaggedObject : public ObjectHeader { +public: + static TaggedObject *Cast(ObjectHeader *header) + { + return static_cast(header); + } + + void SetClass(JSHandle hclass); + + void SynchronizedSetClass(JSHClass *hclass); + JSHClass *SynchronizedGetClass() const; + void SetClass(JSHClass *hclass); + JSHClass *GetClass() const; + + size_t GetObjectSize(); + + // Size of object header + static constexpr int ObjectHeaderSize() + { + return sizeof(TaggedObject); + } +}; +static_assert(TaggedObject::ObjectHeaderSize() == sizeof(MarkWordType)); +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_TAGGED_OBJECT_HEADER_H diff --git a/ecmascript/mem/tlab_allocator-inl.h b/ecmascript/mem/tlab_allocator-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..d92ab43b770e6cd059125582251aa95d49e642c7 --- /dev/null +++ b/ecmascript/mem/tlab_allocator-inl.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_TLAB_ALLOCATOR_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_TLAB_ALLOCATOR_INL_H + +#include + +#include "ecmascript/free_object.h" +#include "ecmascript/mem/compress_collector.h" +#include "ecmascript/mem/semi_space_collector-inl.h" +#include "ecmascript/mem/tlab_allocator.h" + +namespace panda::ecmascript { +static constexpr size_t SEMIGC_YOUNG_BUFFER_SIZE = 32 * 1024; +static constexpr size_t OLD_BUFFER_SIZE = 255 * 1024; + +TlabAllocator::TlabAllocator(Heap *heap, TriggerGCType gcType) + : heap_(heap), gcType_(gcType), youngBegin_(0), youngTop_(0), youngEnd_(0), youngEnable_(true), oldBegin_(0), + oldTop_(0), oldEnd_(0) +{ +} + +TlabAllocator::~TlabAllocator() +{ + FreeObject::FillFreeObject(heap_->GetEcmaVM(), youngTop_, youngEnd_ - youngTop_); + if (gcType_ == TriggerGCType::SEMI_GC) { + heap_->GetSemiSpaceCollector()->oldSpaceAllocator_.Free(oldTop_, oldEnd_); + } else if (gcType_ == TriggerGCType::COMPRESS_FULL_GC) { + heap_->GetCompressCollector()->oldSpaceAllocator_.Free(oldTop_, oldEnd_); + } +} + +uintptr_t TlabAllocator::Allocate(size_t size, SpaceAlloc spaceAlloc) +{ + uintptr_t result = 0; + switch (spaceAlloc) { + case SpaceAlloc::YOUNG_SPACE: + result = TlabAllocatorYoungSpace(size); + break; + case SpaceAlloc::OLD_SPACE: + result = TlabAllocatorOldSpace(size); + break; + default: + UNREACHABLE(); + } + return result; +} + +uintptr_t TlabAllocator::TlabAllocatorYoungSpace(size_t size) +{ + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + if (UNLIKELY(size >= SMALL_OBJECT_SIZE)) { + uintptr_t largeObject = 0; + if (gcType_ == TriggerGCType::SEMI_GC) { + largeObject = heap_->GetSemiSpaceCollector()->AllocateOld(size); + } else { + UNREACHABLE(); + } + return largeObject; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (UNLIKELY(youngTop_ + size > youngEnd_)) { + // set FreeObject and extend + if (youngEnd_ - youngTop_ != 0) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), youngTop_, youngEnd_ - youngTop_); + } + if (!youngEnable_ || !ExpandYoung()) { + youngEnable_ = false; + return 0; + } + } + + uintptr_t result = youngTop_; + + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + youngTop_ += size; + return result; +} + +uintptr_t TlabAllocator::TlabAllocatorOldSpace(size_t size) +{ + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (UNLIKELY(oldTop_ + size > oldEnd_)) { + // set FreeObject and extend + if (oldEnd_ - oldTop_ != 0) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), oldTop_, oldEnd_ - oldTop_); + } + if (!ExpandOld()) { + return 0; + } + } + + uintptr_t result = oldTop_; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + oldTop_ += size; + return result; +} + +bool TlabAllocator::ExpandYoung() +{ + uintptr_t buffer = 0; + if (gcType_ == TriggerGCType::SEMI_GC) { + buffer = heap_->GetSemiSpaceCollector()->AllocateYoung(SEMIGC_YOUNG_BUFFER_SIZE); + } else { + UNREACHABLE(); + } + + if (buffer == 0) { + return false; + } + youngBegin_ = buffer; + youngTop_ = youngBegin_; + youngEnd_ = youngBegin_ + SEMIGC_YOUNG_BUFFER_SIZE; + return true; +} + +bool TlabAllocator::ExpandOld() +{ + uintptr_t buffer = 0; + if (gcType_ == TriggerGCType::SEMI_GC) { + buffer = heap_->GetSemiSpaceCollector()->AllocateOld(OLD_BUFFER_SIZE); + } else if (gcType_ == TriggerGCType::COMPRESS_FULL_GC) { + buffer = heap_->GetCompressCollector()->AllocateOld(OLD_BUFFER_SIZE); + } else { + UNREACHABLE(); + } + + if (buffer == 0) { + return false; + } + oldBegin_ = buffer; + oldTop_ = oldBegin_; + oldEnd_ = oldBegin_ + OLD_BUFFER_SIZE; + return true; +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_TLAB_ALLOCATOR_INL_H diff --git a/ecmascript/mem/tlab_allocator.h b/ecmascript/mem/tlab_allocator.h new file mode 100644 index 0000000000000000000000000000000000000000..11064c60263331b50c034fa5ab71b990f3b72cc2 --- /dev/null +++ b/ecmascript/mem/tlab_allocator.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_TLAB_ALLOCATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_TLAB_ALLOCATOR_H + +namespace panda::ecmascript { +enum class SpaceAlloc : bool { + YOUNG_SPACE, + OLD_SPACE, +}; + +class Heap; + +class TlabAllocator { +public: + TlabAllocator() = delete; + inline ~TlabAllocator(); + NO_COPY_SEMANTIC(TlabAllocator); + NO_MOVE_SEMANTIC(TlabAllocator); + + inline explicit TlabAllocator(Heap *heap, TriggerGCType gcType); + + inline uintptr_t Allocate(size_t size, SpaceAlloc spaceAlloc); + +private: + inline uintptr_t TlabAllocatorYoungSpace(size_t size); + inline uintptr_t TlabAllocatorOldSpace(size_t size); + + inline bool ExpandYoung(); + inline bool ExpandOld(); + + Heap *heap_; + TriggerGCType gcType_; + uintptr_t youngBegin_; + uintptr_t youngTop_; + uintptr_t youngEnd_; + bool youngEnable_; + + uintptr_t oldBegin_; + uintptr_t oldTop_; + uintptr_t oldEnd_; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_TLAB_ALLOCATOR_H diff --git a/ecmascript/mem/verification.cpp b/ecmascript/mem/verification.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dac8f084b86398b5a8de3c2e946474418667caad --- /dev/null +++ b/ecmascript/mem/verification.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "verification.h" + +#include "ecmascript/js_tagged_value-inl.h" +#include "heap_roots-inl.h" +#include "slots.h" + +namespace panda::ecmascript { +// Verify the object body +void VerifyObjectVisitor::VisitAllObjects(TaggedObject *obj) +{ + auto jsHclass = obj->GetClass(); + rootManager_.MarkObjectBody( + obj, jsHclass, [this]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsWeak()) { + if (!HEAP->IsLive(value.GetTaggedWeakRef())) { + LOG(ERROR, RUNTIME) << "Heap verify detected a dead object at " << value.GetTaggedObject(); + ++(*FAIL_COUNT); + } + } else if (value.IsHeapObject()) { + if (!HEAP->IsLive(value.GetTaggedObject())) { + LOG(ERROR, RUNTIME) << "Heap verify detected a dead object at " << value.GetTaggedObject(); + ++(*FAIL_COUNT); + } + } + } + }); +} + +size_t Verification::VerifyRoot() const +{ + size_t failCount = 0; + RootVisitor visit1 = [this, &failCount]([[maybe_unused]] Root type, ObjectSlot slot) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsWeak()) { + VerifyObjectVisitor(heap_, &failCount)(value.GetTaggedWeakRef()); + } else if (value.IsHeapObject()) { + VerifyObjectVisitor(heap_, &failCount)(value.GetTaggedObject()); + } + }; + RootRangeVisitor visit2 = [this, &failCount]([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsWeak()) { + VerifyObjectVisitor(heap_, &failCount)(value.GetTaggedWeakRef()); + } else if (value.IsHeapObject()) { + VerifyObjectVisitor(heap_, &failCount)(value.GetTaggedObject()); + } + } + }; + rootManager_.VisitVMRoots(visit1, visit2); + if (failCount > 0) { + LOG(ERROR, RUNTIME) << "VerifyRoot detects deadObject count is " << failCount; + } + + return failCount; +} + +size_t Verification::VerifyHeap() const +{ + size_t failCount = heap_->VerifyHeapObjects(); + if (failCount > 0) { + LOG(ERROR, RUNTIME) << "VerifyHeap detects deadObject count is " << failCount; + } + return failCount; +} + +bool Verification::IsHeapAddress(void *addr) const +{ + return heap_->ContainObject(reinterpret_cast(addr)); +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/verification.h b/ecmascript/mem/verification.h new file mode 100644 index 0000000000000000000000000000000000000000..4fa42f48471052321b7bdfe92be12cac78e4179f --- /dev/null +++ b/ecmascript/mem/verification.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_VERIFICATION_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_VERIFICATION_H + +#include + +#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/heap_roots.h" +#include "ecmascript/mem/mem.h" +#include "ecmascript/mem/slots.h" + +namespace panda::ecmascript { +// Verify the object body +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) +class VerifyObjectVisitor { +public: + VerifyObjectVisitor(const Heap *heap, size_t *failCount) + : HEAP(heap), FAIL_COUNT(failCount), rootManager_(heap->GetEcmaVM()) + { + } + ~VerifyObjectVisitor() = default; + + void operator()(TaggedObject *obj) + { + VisitAllObjects(obj); + } + + size_t GetFailedCount() const + { + return *FAIL_COUNT; + } + + void VisitAllObjects(TaggedObject *obj); + +private: + const Heap * const HEAP{nullptr}; + size_t * const FAIL_COUNT{nullptr}; + HeapRootManager rootManager_; +}; + +class Verification { +public: + explicit Verification(const Heap *heap) : heap_(heap), rootManager_(heap->GetEcmaVM()) {} + ~Verification() = default; + + bool IsHeapAddress(void *addr) const; + size_t VerifyRoot() const; + + size_t VerifyHeap() const; + + size_t VerifyAll() const + { + return VerifyRoot() + VerifyHeap(); + } + + NO_COPY_SEMANTIC(Verification); + NO_MOVE_SEMANTIC(Verification); + +private: + const Heap *heap_{nullptr}; + HeapRootManager rootManager_; +}; +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_HEAP_VERIFICATION_H diff --git a/ecmascript/message_string.h b/ecmascript/message_string.h new file mode 100644 index 0000000000000000000000000000000000000000..4cce99c3ef0771a49f45dcd939bf34ad86d777f6 --- /dev/null +++ b/ecmascript/message_string.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MESSAGE_STRING_H +#define PANDA_RUNTIME_ECMASCRIPT_MESSAGE_STRING_H + +#include + +namespace kungfu { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MESSAGE_STRING_LIST(V) \ + V(SetReadOnlyProperty, "Cannot set readonly property") \ + V(SetPropertyWhenNotExtensible, "Cannot add property in prevent extensions ") + +class MessageString { +public: + enum MessageId { + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DEF_MESSAGE_ID(name, string) Message_##name, + MESSAGE_STRING_LIST(DEF_MESSAGE_ID) +#undef DEF_MESSAGE_ID + MAX_MESSAGE_COUNT + }; + static std::string GetMessageString(int id); +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GET_MESSAGE_STRING_ID(name) static_cast((MessageString::MessageId::Message_##name)) +} // namespace kungfu +#endif diff --git a/ecmascript/napi/include/jsnapi.h b/ecmascript/napi/include/jsnapi.h new file mode 100644 index 0000000000000000000000000000000000000000..2205c2743412fdba3bf804770cd192a245df8600 --- /dev/null +++ b/ecmascript/napi/include/jsnapi.h @@ -0,0 +1,811 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_NAPI_JSNAPI_H +#define PANDA_RUNTIME_ECMASCRIPT_NAPI_JSNAPI_H + +#include +#include +#include +#include + +#include "ecmascript/common.h" +#include "libpandabase/macros.h" + +namespace panda { +class JSNApiHelper; +class EscapeLocalScope; +template +class Global; +class JSNApi; +class PrimitiveRef; +class ArrayRef; +class StringRef; +class ObjectRef; +class FunctionRef; +class NumberRef; +class BooleanRef; +namespace test { +class JSNApiTests; +} // namespace test + +namespace ecmascript { +class EcmaVM; +} // namespace ecmascript + +using EcmaVM = ecmascript::EcmaVM; +using JSTaggedType = uint64_t; +static constexpr uint32_t DEFAULT_GC_POOL_SIZE = 256 * 1024 * 1024; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DISALLOW_COPY(className) \ + className(const className &) = delete; \ + className &operator=(const className &) = delete + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DISALLOW_MOVE(className) \ + className(className &&) = delete; \ + className &operator=(className &&) = delete + +template +class PUBLIC_API Local { // NOLINT(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) +public: + inline Local() = default; + + template + inline Local(const Local ¤t) : address_(reinterpret_cast(*current)) + { + // Check + } + + Local(const EcmaVM *vm, const Global ¤t); + + ~Local() = default; + + inline T *operator*() const + { + return GetAddress(); + } + + inline T *operator->() const + { + return GetAddress(); + } + + inline bool IsEmpty() const + { + return GetAddress() == nullptr; + } + + inline bool CheckException() const + { + return IsEmpty() || GetAddress()->IsException(); + } + + inline bool IsException() const + { + return !IsEmpty() && GetAddress()->IsException(); + } + + inline bool IsNull() const + { + return IsEmpty() || GetAddress()->IsHole(); + } + +private: + explicit inline Local(uintptr_t addr) : address_(addr) {} + inline T *GetAddress() const + { + return reinterpret_cast(address_); + }; + uintptr_t address_ = 0U; + friend JSNApiHelper; + friend EscapeLocalScope; +}; + +template +class PUBLIC_API Global { // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions +public: + inline Global() = default; + + template + Global(const EcmaVM *vm, const Local ¤t); + ~Global() = default; + + Local ToLocal(const EcmaVM *vm) const + { + return Local(vm, *this); + } + + // This method must be called before Global is released. + void FreeGlobalHandleAddr(); + + inline T *operator*() const + { + return GetAddress(); + } + + inline T *operator->() const + { + return GetAddress(); + } + + inline bool IsEmpty() const + { + return GetAddress() == nullptr; + } + + inline bool CheckException() const + { + return IsEmpty() || GetAddress()->IsException(); + } + +private: + inline T *GetAddress() const + { + return reinterpret_cast(address_); + }; + uintptr_t address_ = 0U; + const EcmaVM *vm_{nullptr}; +}; + +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) +class PUBLIC_API LocalScope { +public: + explicit LocalScope(const EcmaVM *vm); + virtual ~LocalScope(); + +protected: + inline LocalScope(const EcmaVM *vm, JSTaggedType value); + +private: + void *prevNext_ = nullptr; + void *prevEnd_ = nullptr; + void *thread_ = nullptr; +}; + +class PUBLIC_API EscapeLocalScope final : public LocalScope { +public: + explicit EscapeLocalScope(const EcmaVM *vm); + ~EscapeLocalScope() override = default; + + DISALLOW_COPY(EscapeLocalScope); + DISALLOW_MOVE(EscapeLocalScope); + + template + inline Local Escape(Local current) + { + ASSERT(!alreadyEscape_); + alreadyEscape_ = true; + *(reinterpret_cast(escapeHandle_)) = **current; + return Local(escapeHandle_); + } + +private: + bool alreadyEscape_ = false; + uintptr_t escapeHandle_ = 0U; +}; + +class PUBLIC_API JSExecutionScope { +public: + explicit JSExecutionScope(const EcmaVM *vm); + ~JSExecutionScope(); + DISALLOW_COPY(JSExecutionScope); + DISALLOW_MOVE(JSExecutionScope); + +private: + void *last_current_thread_ = nullptr; + bool is_revert_ = false; +}; + +class PUBLIC_API JSValueRef { +public: + static Local Undefined(const EcmaVM *vm); + static Local Null(const EcmaVM *vm); + static Local True(const EcmaVM *vm); + static Local False(const EcmaVM *vm); + static Local Exception(const EcmaVM *vm); + + bool BooleaValue(); + int64_t IntegerValue(const EcmaVM *vm); + uint32_t Uint32Value(const EcmaVM *vm); + int32_t Int32Value(const EcmaVM *vm); + + Local ToNumber(const EcmaVM *vm); + Local ToBoolean(const EcmaVM *vm); + Local ToString(const EcmaVM *vm); + Local ToObject(const EcmaVM *vm); + + bool IsUndefined(); + bool IsNull(); + bool IsHole(); + bool IsTrue(); + bool IsFalse(); + bool IsNumber(); + bool IsInt(); + bool WithinInt32(); + bool IsBoolean(); + bool IsString(); + bool IsSymbol(); + bool IsObject(); + bool IsArray(const EcmaVM *vm); + bool IsConstructor(); + bool IsFunction(); + bool IsProxy(); + bool IsException(); + bool IsPromise(); + bool IsDataView(); + bool IsTypedArray(); + bool IsNativePointer(); + bool IsNativeObject(); + bool IsDate(); + bool IsError(); + bool IsMap(); + bool IsSet(); + bool IsWeakMap(); + bool IsWeakSet(); + bool IsRegExp(); + bool IsArrayIterator(); + bool IsStringIterator(); + bool IsSetIterator(); + bool IsMapIterator(); + bool IsArrayBuffer(); + bool IsUint8Array(); + bool IsInt8Array(); + bool IsUint8ClampedArray(); + bool IsInt16Array(); + bool IsUint16Array(); + bool IsInt32Array(); + bool IsUint32Array(); + bool IsFloat32Array(); + bool IsFloat64Array(); + + bool IsStrictEquals(const EcmaVM *vm, Local value); + Local Typeof(const EcmaVM *vm); + bool InstanceOf(const EcmaVM *vm, Local value); + +private: + JSTaggedType value_; + friend JSNApi; + template + friend class Global; + template + friend class Local; +}; + +class PUBLIC_API PrimitiveRef : public JSValueRef { +}; + +class PUBLIC_API IntegerRef : public PrimitiveRef { +public: + static Local New(const EcmaVM *vm, int input); + int Value(); +}; + +class PUBLIC_API NumberRef : public PrimitiveRef { +public: + static Local New(const EcmaVM *vm, double input); + double Value(); +}; + +class PUBLIC_API BooleanRef : public PrimitiveRef { +public: + static Local New(const EcmaVM *vm, bool input); + bool Value(); +}; + +class PUBLIC_API StringRef : public PrimitiveRef { +public: + static inline StringRef *Cast(JSValueRef *value) + { + // check + return static_cast(value); + } + static Local NewFromUtf8(const EcmaVM *vm, const char *utf8, int length = -1); + std::string ToString(); + int32_t Length(); + int32_t Utf8Length(); + int WriteUtf8(char *buffer, int length); +}; + +class PUBLIC_API SymbolRef : public PrimitiveRef { +public: + static Local New(const EcmaVM *vm, Local description); + Local GetDescription(const EcmaVM *vm); +}; + +using NativePointerCallback = void (*)(void* value, void* hint); +class PUBLIC_API NativePointerRef : public JSValueRef { +public: + static Local New(const EcmaVM *vm, void *nativePointer); + static Local New(const EcmaVM *vm, + void *nativePointer, + NativePointerCallback callBack, + void *data); + void *Value(); +}; + +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) +class PUBLIC_API PropertyAttribute { +public: + static PropertyAttribute Default() + { + return PropertyAttribute(); + } + PropertyAttribute() = default; + PropertyAttribute(Local value, bool w, bool e, bool c) + : value_(value), + writable_(w), + enumerable_(e), + configurable_(c), + hasWritable_(true), + hasEnumerable_(true), + hasConfigurable_(true) + {} + ~PropertyAttribute() = default; + + bool IsWritable() const + { + return writable_; + } + void SetWritable(bool flag) + { + writable_ = flag; + hasWritable_ = true; + } + bool IsEnumerable() const + { + return enumerable_; + } + void SetEnumerable(bool flag) + { + enumerable_ = flag; + hasEnumerable_ = true; + } + bool IsConfigurable() const + { + return configurable_; + } + void SetConfigurable(bool flag) + { + configurable_ = flag; + hasConfigurable_ = true; + } + bool HasWritable() const + { + return hasWritable_; + } + bool HasConfigurable() const + { + return hasConfigurable_; + } + bool HasEnumerable() const + { + return hasEnumerable_; + } + Local GetValue(const EcmaVM *vm) const + { + if (value_.IsEmpty()) { + return JSValueRef::Undefined(vm); + } + return value_; + } + void SetValue(Local value) + { + value_ = value; + } + inline bool HasValue() const + { + return !value_.IsEmpty(); + } + Local GetGetter(const EcmaVM *vm) const + { + if (getter_.IsEmpty()) { + return JSValueRef::Undefined(vm); + } + return getter_; + } + void SetGetter(Local value) + { + getter_ = value; + } + bool HasGetter() const + { + return !getter_.IsEmpty(); + } + Local GetSetter(const EcmaVM *vm) const + { + if (setter_.IsEmpty()) { + return JSValueRef::Undefined(vm); + } + return setter_; + } + void SetSetter(Local value) + { + setter_ = value; + } + bool HasSetter() const + { + return !setter_.IsEmpty(); + } + +private: + Local value_; + Local getter_; + Local setter_; + bool writable_ = false; + bool enumerable_ = false; + bool configurable_ = false; + bool hasWritable_ = false; + bool hasEnumerable_ = false; + bool hasConfigurable_ = false; +}; + +class PUBLIC_API ObjectRef : public JSValueRef { +public: + static inline ObjectRef *Cast(JSValueRef *value) + { + // check + return static_cast(value); + } + static Local New(const EcmaVM *vm); + bool Set(const EcmaVM *vm, Local key, Local value); + bool Set(const EcmaVM *vm, uint32_t key, Local value); + bool SetAccessorProperty(const EcmaVM *vm, Local key, Local getter, + Local setter, PropertyAttribute attribute = PropertyAttribute::Default()); + Local Get(const EcmaVM *vm, Local key); + Local Get(const EcmaVM *vm, int32_t key); + + bool GetOwnProperty(const EcmaVM *vm, Local key, PropertyAttribute &property); + Local GetOwnPropertyNames(const EcmaVM *vm); + Local GetOwnEnumerablePropertyNames(const EcmaVM *vm); + Local GetPrototype(const EcmaVM *vm); + + bool DefineProperty(const EcmaVM *vm, Local key, PropertyAttribute attribute); + + bool Has(const EcmaVM *vm, Local key); + bool Has(const EcmaVM *vm, uint32_t key); + + bool Delete(const EcmaVM *vm, Local key); + bool Delete(const EcmaVM *vm, uint32_t key); + // Private +}; + +using FunctionCallback = Local (*)(EcmaVM *, Local, + const Local[], // NOLINTNEXTLINE(modernize-avoid-c-arrays) + int32_t, void *); +using FunctionCallbackWithNewTarget = + Local (*)(EcmaVM *, Local, Local, const Local[], int32_t, void *); +class PUBLIC_API FunctionRef : public ObjectRef { +public: + static Local New(EcmaVM *vm, FunctionCallback nativeFunc, void *data); + static Local NewClassFunction(EcmaVM *vm, FunctionCallbackWithNewTarget nativeFunc, void *data); + Local Call(const EcmaVM *vm, Local thisObj, const Local argv[], + int32_t length); + Local Constructor(const EcmaVM *vm, const Local argv[], int32_t length); + Local GetFunctionPrototype(const EcmaVM *vm); + void SetName(const EcmaVM *vm, Local name); + Local GetName(const EcmaVM *vm); + bool IsNative(const EcmaVM *vm); +}; + +class PUBLIC_API ArrayRef : public ObjectRef { +public: + static Local New(const EcmaVM *vm, int32_t length = 0); + int32_t Length(const EcmaVM *vm); +}; + +class PUBLIC_API PromiseRef : public ObjectRef { +public: + Local Catch(const EcmaVM *vm, Local handler); + Local Then(const EcmaVM *vm, Local handler); + Local Then(const EcmaVM *vm, Local onFulfilled, Local onRejected); +}; + +class PUBLIC_API PromiseCapabilityRef : public ObjectRef { +public: + static Local New(const EcmaVM *vm); + bool Resolve(const EcmaVM *vm, Local value); + bool Reject(const EcmaVM *vm, Local reason); + Local GetPromise(const EcmaVM *vm); +}; + +using Deleter = void (*)(void *buffer, void *data); +class PUBLIC_API ArrayBufferRef : public ObjectRef { +public: + static Local New(const EcmaVM *vm, int32_t length); + static Local New(const EcmaVM *vm, void *buffer, int32_t length, const Deleter &deleter, + void *data); + + int32_t ByteLength(const EcmaVM *vm); + void *GetBuffer(); +}; + +class PUBLIC_API DataViewRef : public ObjectRef { +public: + static Local New(const EcmaVM *vm, Local arrayBuffer, int32_t byteOffset, + int32_t byteLength); + int32_t ByteLength(); + int32_t ByteOffset(); + Local GetArrayBuffer(const EcmaVM *vm); +}; + +class PUBLIC_API TypedArrayRef : public ObjectRef { +public: + int32_t ByteLength(const EcmaVM *vm); + int32_t ByteOffset(const EcmaVM *vm); + int32_t ArrayLength(const EcmaVM *vm); + Local GetArrayBuffer(const EcmaVM *vm); +}; + +class PUBLIC_API Int8ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, int32_t length); +}; + +class PUBLIC_API Uint8ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, int32_t length); +}; + +class PUBLIC_API Uint8ClampedArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, + int32_t length); +}; + +class PUBLIC_API Int16ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, int32_t length); +}; + +class PUBLIC_API Uint16ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, + int32_t length); +}; + +class PUBLIC_API Int32ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, int32_t length); +}; + +class PUBLIC_API Uint32ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, + int32_t length); +}; + +class PUBLIC_API Float32ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, + int32_t length); +}; + +class PUBLIC_API Float64ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, + int32_t length); +}; + +class PUBLIC_API RegExpRef : public ObjectRef { +public: + Local GetOriginalSource(const EcmaVM *vm); +}; + +class PUBLIC_API DateRef : public ObjectRef { +public: + Local ToString(const EcmaVM *vm); +}; + +class PUBLIC_API MapRef : public ObjectRef { +public: + int32_t GetSize(); +}; + +class PUBLIC_API SetRef : public ObjectRef { +public: + int32_t GetSize(); +}; + +class PUBLIC_API JSON { +public: + static Local Parse(const EcmaVM *vm, Local string); + static Local Stringify(const EcmaVM *vm, Local json); +}; + +class PUBLIC_API Exception { +public: + static Local Error(const EcmaVM *vm, Local message); + static Local RangeError(const EcmaVM *vm, Local message); + static Local ReferenceError(const EcmaVM *vm, Local message); + static Local SyntaxError(const EcmaVM *vm, Local message); + static Local TypeError(const EcmaVM *vm, Local message); + static Local EvalError(const EcmaVM *vm, Local message); +}; + +using LOG_PRINT = int (*)(int id, int level, const char *tag, const char *fmt, const char *message); + +class PUBLIC_API RuntimeOption { +public: + enum class PUBLIC_API GC_TYPE : uint8_t { EPSILON, GEN_GC, STW }; + enum class PUBLIC_API LOG_LEVEL : uint8_t { + DEBUG = 3, + INFO = 4, + WARN = 5, + ERROR = 6, + FATAL = 7, + }; + + void SetGcType(GC_TYPE type) + { + gcType_ = type; + } + + void SetGcPoolSize(uint32_t size) + { + gcPoolSize_ = size; + } + + void SetLogLevel(LOG_LEVEL logLevel) + { + logLevel_ = logLevel; + } + + void SetLogBufPrint(LOG_PRINT out) + { + logBufPrint_ = out; + } + + void SetDebuggerLibraryPath(const std::string &path) + { + debuggerLibraryPath_ = path; + } + +private: + std::string GetGcType() const + { + std::string gcType; + switch (gcType_) { + case GC_TYPE::GEN_GC: + gcType = "gen-gc"; + break; + case GC_TYPE::STW: + gcType = "stw"; + break; + case GC_TYPE::EPSILON: + gcType = "epsilon"; + break; + default: + UNREACHABLE(); + } + return gcType; + } + + std::string GetLogLevel() const + { + std::string logLevel; + switch (logLevel_) { + case LOG_LEVEL::INFO: + case LOG_LEVEL::WARN: + logLevel = "info"; + break; + case LOG_LEVEL::ERROR: + logLevel = "error"; + break; + case LOG_LEVEL::FATAL: + logLevel = "fatal"; + break; + case LOG_LEVEL::DEBUG: + default: + logLevel = "debug"; + break; + } + + return logLevel; + } + + uint32_t GetGcPoolSize() const + { + return gcPoolSize_; + } + + LOG_PRINT GetLogBufPrint() const + { + return logBufPrint_; + } + + std::string GetDebuggerLibraryPath() const + { + return debuggerLibraryPath_; + } + + GC_TYPE gcType_ = GC_TYPE::EPSILON; + LOG_LEVEL logLevel_ = LOG_LEVEL::DEBUG; + uint32_t gcPoolSize_ = DEFAULT_GC_POOL_SIZE; + LOG_PRINT logBufPrint_{nullptr}; + std::string debuggerLibraryPath_{}; + friend JSNApi; +}; + +class PUBLIC_API JSNApi { +public: + // JSVM + static EcmaVM *CreateJSVM(const RuntimeOption &option); + static void DestoryJSVM(EcmaVM *ecmaVm); + + // JS code + static bool Execute(EcmaVM *vm, Local fileName, Local entry); + static bool Execute(EcmaVM *vm, const uint8_t *data, int32_t size, Local entry); + static bool ExecuteModuleFromBuffer(EcmaVM *vm, const void *data, int32_t size, const std::string &file); + static Local GetExportObject(EcmaVM *vm, const std::string &file, const std::string &itemName); + + // ObjectRef Operation + static Local GetGlobalObject(const EcmaVM *vm); + static void ExecutePendingJob(const EcmaVM *vm); + + // Memory + static void TriggerGC(const EcmaVM *vm); + + // Exception + static void ThrowException(const EcmaVM *vm, Local error); + static Local GetUncaughtException(const EcmaVM *vm); + static void EnableUserUncaughtErrorHandler(EcmaVM *vm); + + static bool StartDebugger(const char *library_path, EcmaVM *vm); + // Serialize & Deserialize. + static void* SerializeValue(const EcmaVM *vm, Local data, Local transfer); + static Local DeserializeValue(const EcmaVM *vm, void* recoder); + static void DeleteSerializationData(void *data); + +private: + static bool CreateRuntime(const RuntimeOption &option); + static bool DestoryRuntime(); + + static uintptr_t GetHandleAddr(const EcmaVM *vm, uintptr_t localAddress); + static uintptr_t GetGlobalHandleAddr(const EcmaVM *vm, uintptr_t localAddress); + static void DisposeGlobalHandleAddr(const EcmaVM *vm, uintptr_t addr); + template + friend class Global; + template + friend class Local; + friend class test::JSNApiTests; +}; + +template +template +Global::Global(const EcmaVM *vm, const Local ¤t) : vm_(vm) +{ + address_ = JSNApi::GetGlobalHandleAddr(vm_, reinterpret_cast(*current)); +} + +template +void Global::FreeGlobalHandleAddr() +{ + if (address_ == 0) { + return; + } + JSNApi::DisposeGlobalHandleAddr(vm_, address_); + address_ = 0; +} + +// ---------------------------------- Local -------------------------------------------- +template +Local::Local(const EcmaVM *vm, const Global ¤t) +{ + address_ = JSNApi::GetHandleAddr(vm, reinterpret_cast(*current)); +} +} // namespace panda +#endif // PANDA_RUNTIME_ECMASCRIPT_JSNAPI_JSNAPI_H diff --git a/ecmascript/napi/jsnapi.cpp b/ecmascript/napi/jsnapi.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e682749fe5118d90857bc8e23d94a95fc3421ec3 --- /dev/null +++ b/ecmascript/napi/jsnapi.cpp @@ -0,0 +1,1588 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jsnapi_helper-inl.h" + +#include +#include + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/base/json_parser.h" +#include "ecmascript/base/json_stringifier.h" +#include "ecmascript/base/string_helper.h" +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/ecma_language_context.h" +#include "ecmascript/ecma_module.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_dataview.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_map.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_regexp.h" +#include "ecmascript/js_serializer.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/js_typed_array.h" +#include "ecmascript/mem/region.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array.h" +#include "libpandabase/os/library_loader.h" +#include "utils/pandargs.h" + +namespace panda { +using ecmascript::CString; +using ecmascript::EcmaString; +using ecmascript::ErrorType; +using ecmascript::FastRuntimeStub; +using ecmascript::GlobalEnv; +using ecmascript::GlobalEnvConstants; +using ecmascript::JSArray; +using ecmascript::JSArrayBuffer; +using ecmascript::JSDataView; +using ecmascript::JSDate; +using ecmascript::JSFunction; +using ecmascript::JSFunctionBase; +using ecmascript::JSFunctionExtraInfo; +using ecmascript::JSHClass; +using ecmascript::JSMap; +using ecmascript::JSMethod; +using ecmascript::JSNativeObject; +using ecmascript::JSNativePointer; +using ecmascript::JSObject; +using ecmascript::JSPromise; +using ecmascript::JSRegExp; +using ecmascript::JSSerializer; +using ecmascript::JSSet; +using ecmascript::JSSymbol; +using ecmascript::JSTaggedNumber; +using ecmascript::JSTaggedType; +using ecmascript::JSTaggedValue; +using ecmascript::JSThread; +using ecmascript::ObjectFactory; +using ecmascript::PromiseCapability; +using ecmascript::PropertyDescriptor; +using ecmascript::OperationResult; +using ecmascript::Region; +using ecmascript::TaggedArray; +using ecmascript::base::BuiltinsBase; +using ecmascript::base::JsonParser; +using ecmascript::base::JsonStringifier; +using ecmascript::base::StringHelper; +using ecmascript::base::TypedArrayHelper; +template +using JSHandle = ecmascript::JSHandle; + +static const uint32_t INTERNAL_POOL_SIZE = 26214400; +static const uint32_t CODE_POOL_SIZE = 10485760; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static const std::string_view ENTRY_POINTER = "_GLOBAL::func_main_0"; + +// ------------------------------------ Panda ----------------------------------------------- +bool JSNApi::CreateRuntime(const RuntimeOption &option) +{ + RuntimeOptions runtimeOptions; + runtimeOptions.SetRuntimeType("ecmascript"); + + // GC + runtimeOptions.SetGcType(option.GetGcType()); + runtimeOptions.SetRunGcInPlace(true); + + // Mem + runtimeOptions.SetObjectPoolSize(option.GetGcPoolSize()); + runtimeOptions.SetInternalPoolSize(INTERNAL_POOL_SIZE); + runtimeOptions.SetCodePoolSize(CODE_POOL_SIZE); + + // Boot + runtimeOptions.SetShouldLoadBootPandaFiles(false); + runtimeOptions.SetShouldInitializeIntrinsics(false); + runtimeOptions.SetBootClassSpaces({"ecmascript"}); + + // Dfx + runtimeOptions.SetLogLevel(option.GetLogLevel()); + arg_list_t logComponents; + logComponents.emplace_back("all"); + runtimeOptions.SetLogComponents(logComponents); + if (option.GetLogBufPrint() != nullptr) { + runtimeOptions.SetMobileLog(reinterpret_cast(option.GetLogBufPrint())); + } + + // Debugger + runtimeOptions.SetDebuggerLibraryPath(option.GetDebuggerLibraryPath()); + + static EcmaLanguageContext lcEcma; + if (!Runtime::Create(runtimeOptions, {&lcEcma})) { + std::cerr << "Error: cannot create runtime" << std::endl; + return false; + } + return true; +} + +bool JSNApi::DestoryRuntime() +{ + return Runtime::Destroy(); +} + +EcmaVM *JSNApi::CreateJSVM(const RuntimeOption &option) +{ + auto runtime = Runtime::GetCurrent(); + if (runtime == nullptr) { + // Art java + Panda js or L2 pure JS app + if (!CreateRuntime(option)) { + return nullptr; + } + runtime = Runtime::GetCurrent(); + return EcmaVM::Cast(runtime->GetPandaVM()); + } + RuntimeOptions runtimeOptions; + + // GC + runtimeOptions.SetGcTriggerType("no-gc-for-start-up"); // A non-production gc strategy. Prohibit stw-gc 10 times. + + return EcmaVM::Cast(EcmaVM::Create(runtimeOptions)); +} + +void JSNApi::DestoryJSVM(EcmaVM *ecmaVm) +{ + auto runtime = Runtime::GetCurrent(); + if (runtime != nullptr) { + PandaVM *mainVm = runtime->GetPandaVM(); + // Art java + Panda js + if (mainVm == ecmaVm) { + DestoryRuntime(); + } else { + EcmaVM::Destroy(ecmaVm); + } + } +} + +void JSNApi::TriggerGC(const EcmaVM *vm) +{ + if (vm->GetJSThread() != nullptr && vm->IsInitialized()) { + vm->CollectGarbage(ecmascript::TriggerGCType::SEMI_GC); + } +} + +void JSNApi::ThrowException(const EcmaVM *vm, Local error) +{ + auto thread = vm->GetJSThread(); + thread->SetException(JSNApiHelper::ToJSTaggedValue(*error)); +} + +bool JSNApi::StartDebugger(const char *library_path, EcmaVM *vm) +{ + auto handle = panda::os::library_loader::Load(std::string(library_path)); + if (!handle) { + return false; + } + + using StartDebugger = bool (*)(const std::string &, EcmaVM *); + + auto sym = panda::os::library_loader::ResolveSymbol(handle.Value(), "StartDebug"); + if (!sym) { + LOG(ERROR, RUNTIME) << sym.Error().ToString(); + return false; + } + + bool ret = reinterpret_cast(sym.Value())("PandaDebugger", vm); + if (ret) { + auto runtime = Runtime::GetCurrent(); + runtime->SetDebugMode(true); + runtime->SetDebuggerLibrary(std::move(handle.Value())); + } + return ret; +} + +bool JSNApi::Execute(EcmaVM *vm, Local fileName, Local entry) +{ + std::string file = fileName->ToString(); + std::string entryPoint = entry->ToString(); + std::vector argv; + LOG_ECMA(DEBUG) << "start to execute ark file" << file; + if (!vm->ExecuteFromPf(file, entryPoint, argv)) { + LOG_ECMA(ERROR) << "Cannot execute ark file" << file; + std::cerr << "Cannot execute ark file '" << file << "' with entry '" << entryPoint << "'" << std::endl; + return false; + } + + return true; +} + +bool JSNApi::Execute(EcmaVM *vm, const uint8_t *data, int32_t size, Local entry) +{ + std::string entryPoint = entry->ToString(); + std::vector argv; + if (!vm->ExecuteFromBuffer(data, size, entryPoint, argv)) { + std::cerr << "Cannot execute panda file from memory " + << "' with entry '" << entryPoint << "'" << std::endl; + return false; + } + + return true; +} + +Local JSNApi::GetUncaughtException(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal(vm->GetEcmaUncaughtException()); +} + +void JSNApi::EnableUserUncaughtErrorHandler(EcmaVM *vm) +{ + return vm->EnableUserUncaughtErrorHandler(); +} + +Local JSNApi::GetGlobalObject(const EcmaVM *vm) +{ + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(vm->GetJSThread(), globalEnv->GetGlobalObject()); + return JSNApiHelper::ToLocal(global); +} + +void JSNApi::ExecutePendingJob(const EcmaVM *vm) +{ + vm->ExecutePromisePendingJob(); +} + +uintptr_t JSNApi::GetHandleAddr(const EcmaVM *vm, uintptr_t localAddress) +{ + if (localAddress == 0) { + return 0; + } + JSTaggedType value = *(reinterpret_cast(localAddress)); + return ecmascript::EcmaHandleScope::NewHandle(vm->GetJSThread(), value); +} + +uintptr_t JSNApi::GetGlobalHandleAddr(const EcmaVM *vm, uintptr_t localAddress) +{ + if (localAddress == 0) { + return 0; + } + JSTaggedType value = *(reinterpret_cast(localAddress)); + return vm->GetJSThread()->GetGlobalHandleStorage()->NewGlobalHandle(value); +} + +void JSNApi::DisposeGlobalHandleAddr(const EcmaVM *vm, uintptr_t addr) +{ + if (addr == 0) { + return; + } + vm->GetJSThread()->GetGlobalHandleStorage()->DisposeGlobalHandle(addr); +} + +void *JSNApi::SerializeValue(const EcmaVM *vm, Local value, Local transfer) +{ + ecmascript::JSThread *thread = vm->GetJSThread(); + ecmascript::Serializer serializer(thread); + JSHandle arkValue = JSNApiHelper::ToJSHandle(value); + JSHandle arkTransfer = JSNApiHelper::ToJSHandle(transfer); + std::unique_ptr data; + if (serializer.WriteValue(thread, arkValue, arkTransfer)) { + data = serializer.Release(); + } + return reinterpret_cast(data.release()); +} + +Local JSNApi::DeserializeValue(const EcmaVM *vm, void *recoder) +{ + ecmascript::JSThread *thread = vm->GetJSThread(); + std::unique_ptr data(reinterpret_cast(recoder)); + ecmascript::Deserializer deserializer(thread, data.release()); + JSHandle result = deserializer.ReadValue(); + return JSNApiHelper::ToLocal(result); +} + +void JSNApi::DeleteSerializationData(void *data) +{ + ecmascript::SerializationData *value = reinterpret_cast(data); + delete value; +} + +bool JSNApi::ExecuteModuleFromBuffer(EcmaVM *vm, const void *data, int32_t size, const std::string &file) +{ + auto moduleManager = vm->GetModuleManager(); + moduleManager->SetCurrentExportModuleName(file); + // Update Current Module + vm->GetJSThread()->SetIsEcmaInterpreter(true); + std::vector argv; + if (!vm->ExecuteFromBuffer(data, size, ENTRY_POINTER, argv)) { + std::cerr << "Cannot execute panda file from memory" << std::endl; + moduleManager->RestoreCurrentExportModuleName(); + return false; + } + + // Restore Current Module + moduleManager->RestoreCurrentExportModuleName(); + return true; +} + +Local JSNApi::GetExportObject(EcmaVM *vm, const std::string &file, const std::string &itemName) +{ + auto moduleManager = vm->GetModuleManager(); + ObjectFactory *factory = vm->GetFactory(); + JSHandle moduleName(factory->NewFromStdString(file)); + JSHandle moduleObj = moduleManager->GetModule(vm->GetJSThread(), moduleName); + JSHandle itemString(factory->NewFromStdString(itemName)); + JSHandle exportObj = moduleManager->GetModuleItem(vm->GetJSThread(), moduleObj, itemString); + return JSNApiHelper::ToLocal(exportObj); +} +// ----------------------------------- HandleScope ------------------------------------- +LocalScope::LocalScope(const EcmaVM *vm) : thread_(vm->GetJSThread()) +{ + auto thread = reinterpret_cast(thread_); + prevNext_ = thread->GetHandleScopeStorageNext(); + prevEnd_ = thread->GetHandleScopeStorageEnd(); +} + +LocalScope::LocalScope(const EcmaVM *vm, JSTaggedType value) : thread_(vm->GetJSThread()) +{ + auto thread = reinterpret_cast(thread_); + prevNext_ = thread->GetHandleScopeStorageNext(); + prevEnd_ = thread->GetHandleScopeStorageEnd(); + ecmascript::EcmaHandleScope::NewHandle(thread, value); +} + +LocalScope::~LocalScope() +{ + auto thread = reinterpret_cast(thread_); + thread->SetHandleScopeStorageNext(static_cast(prevNext_)); + if (thread->GetHandleScopeStorageEnd() != prevEnd_) { + thread->SetHandleScopeStorageEnd(static_cast(prevEnd_)); + thread->ShrunkHandleStorage(static_cast(prevEnd_)); + } +} + +// ----------------------------------- EscapeLocalScope ------------------------------ +EscapeLocalScope::EscapeLocalScope(const EcmaVM *vm) : LocalScope(vm, 0U) +{ + auto thread = vm->GetJSThread(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + escapeHandle_ = ToUintPtr(thread->GetHandleScopeStorageNext() - 1); +} + +// ----------------------------------- NumberRef --------------------------------------- +Local NumberRef::New(const EcmaVM *vm, double input) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle number(thread, JSTaggedValue(input)); + return JSNApiHelper::ToLocal(number); +} + +double NumberRef::Value() +{ + return JSTaggedNumber(JSNApiHelper::ToJSTaggedValue(this)).GetNumber(); +} + +// ----------------------------------- BooleanRef --------------------------------------- +Local BooleanRef::New(const EcmaVM *vm, bool input) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle boolean(thread, JSTaggedValue(input)); + return JSNApiHelper::ToLocal(boolean); +} + +bool BooleanRef::Value() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsTrue(); +} + +// ----------------------------------- IntegerRef --------------------------------------- +Local IntegerRef::New(const EcmaVM *vm, int input) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle integer(thread, JSTaggedValue(input)); + return JSNApiHelper::ToLocal(integer); +} + +int IntegerRef::Value() +{ + return JSNApiHelper::ToJSTaggedValue(this).GetInt(); +} + +// ----------------------------------- StringRef ---------------------------------------- +Local StringRef::NewFromUtf8(const EcmaVM *vm, const char *utf8, int length) +{ + ObjectFactory *factory = vm->GetFactory(); + if (length < 0) { + JSHandle current(factory->NewFromString(utf8)); + return JSNApiHelper::ToLocal(current); + } + JSHandle current(factory->NewFromUtf8(reinterpret_cast(utf8), length)); + return JSNApiHelper::ToLocal(current); +} + +std::string StringRef::ToString() +{ + return StringHelper::ToStdString(EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())); +} + +int32_t StringRef::Length() +{ + return EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())->GetLength(); +} + +int32_t StringRef::Utf8Length() +{ + return EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())->GetUtf8Length(); +} + +int StringRef::WriteUtf8(char *buffer, int length) +{ + return EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject()) + ->CopyDataUtf8(reinterpret_cast(buffer), length); +} + +// ----------------------------------- SymbolRef ----------------------------------------- +Local SymbolRef::New(const EcmaVM *vm, Local description) +{ + ObjectFactory *factory = vm->GetFactory(); + JSHandle symbol = factory->NewJSSymbol(); + JSTaggedValue desc = JSNApiHelper::ToJSTaggedValue(*description); + symbol->SetDescription(vm->GetJSThread(), desc); + return JSNApiHelper::ToLocal(JSHandle(symbol)); +} + +Local SymbolRef::GetDescription(const EcmaVM *vm) +{ + JSTaggedValue description = JSSymbol::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())->GetDescription(); + if (!description.IsString()) { + auto constants = vm->GetJSThread()->GlobalConstants(); + return JSNApiHelper::ToLocal(constants->GetHandledEmptyString()); + } + JSHandle descriptionHandle(vm->GetJSThread(), description); + return JSNApiHelper::ToLocal(descriptionHandle); +} + +// -------------------------------- NativePointerRef ------------------------------------ +Local NativePointerRef::New(const EcmaVM *vm, void *nativePointer) +{ + ObjectFactory *factory = vm->GetFactory(); + JSHandle obj = factory->NewJSNativeObject(nativePointer); + return JSNApiHelper::ToLocal(JSHandle(obj)); +} + +Local NativePointerRef::New( + const EcmaVM *vm, void *nativePointer, NativePointerCallback callBack, void *data) +{ + ObjectFactory *factory = vm->GetFactory(); + JSHandle obj = factory->NewJSNativeObject(nativePointer, callBack, data); + return JSNApiHelper::ToLocal(JSHandle(obj)); +} + +void *NativePointerRef::Value() +{ + JSHandle nativePointer = JSNApiHelper::ToJSHandle(this); + return JSHandle(nativePointer)->GetExternalPointer(); +} + +// ----------------------------------- ObjectRef ---------------------------------------- +Local ObjectRef::New(const EcmaVM *vm) +{ + ObjectFactory *factory = vm->GetFactory(); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle constructor = globalEnv->GetObjectFunction(); + JSHandle object(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + RETURN_VALUE_IF_ABRUPT(vm->GetJSThread(), JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(object); +} + +bool ObjectRef::Set(const EcmaVM *vm, Local key, Local value) +{ + JSHandle obj = JSNApiHelper::ToJSHandle(this); + JSHandle keyValue = JSNApiHelper::ToJSHandle(key); + JSHandle valueValue = JSNApiHelper::ToJSHandle(value); + bool result = JSTaggedValue::SetProperty(vm->GetJSThread(), obj, keyValue, valueValue); + RETURN_VALUE_IF_ABRUPT(vm->GetJSThread(), false); + return result; +} + +bool ObjectRef::Set(const EcmaVM *vm, uint32_t key, Local value) +{ + Local keyValue = NumberRef::New(vm, key); + return Set(vm, keyValue, value); +} + +bool ObjectRef::SetAccessorProperty(const EcmaVM *vm, Local key, Local getter, + Local setter, PropertyAttribute attribute) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle getterValue = JSNApiHelper::ToJSHandle(getter); + JSHandle setterValue = JSNApiHelper::ToJSHandle(setter); + PropertyDescriptor desc(thread, attribute.IsWritable(), attribute.IsEnumerable(), attribute.IsConfigurable()); + desc.SetValue(JSNApiHelper::ToJSHandle(attribute.GetValue(vm))); + desc.SetSetter(setterValue); + desc.SetGetter(getterValue); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + JSHandle keyValue = JSNApiHelper::ToJSHandle(key); + bool result = JSTaggedValue::DefineOwnProperty(thread, obj, keyValue, desc); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +Local ObjectRef::Get(const EcmaVM *vm, Local key) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + JSHandle keyValue = JSNApiHelper::ToJSHandle(key); + OperationResult ret = JSTaggedValue::GetProperty(thread, obj, keyValue); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + if (!ret.GetPropertyMetaData().IsFound()) { + return Local(); + } + return JSNApiHelper::ToLocal(ret.GetValue()); +} + +Local ObjectRef::Get(const EcmaVM *vm, int32_t key) +{ + Local keyValue = IntegerRef::New(vm, key); + return Get(vm, keyValue); +} + +bool ObjectRef::GetOwnProperty(const EcmaVM *vm, Local key, PropertyAttribute &property) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + JSHandle keyValue = JSNApiHelper::ToJSHandle(key); + PropertyDescriptor desc(thread); + bool ret = JSObject::GetOwnProperty(thread, JSHandle(obj), keyValue, desc); + if (!ret) { + return false; + } + property.SetValue(JSNApiHelper::ToLocal(desc.GetValue())); + if (desc.HasGetter()) { + property.SetGetter(JSNApiHelper::ToLocal(desc.GetGetter())); + } + if (desc.HasSetter()) { + property.SetSetter(JSNApiHelper::ToLocal(desc.GetSetter())); + } + if (desc.HasWritable()) { + property.SetWritable(desc.IsWritable()); + } + if (desc.HasEnumerable()) { + property.SetEnumerable(desc.IsEnumerable()); + } + if (desc.HasConfigurable()) { + property.SetConfigurable(desc.IsConfigurable()); + } + + return true; +} + +Local ObjectRef::GetOwnPropertyNames(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle obj(JSNApiHelper::ToJSHandle(this)); + JSHandle array(JSTaggedValue::GetOwnPropertyKeys(thread, obj)); + JSHandle jsArray(JSArray::CreateArrayFromList(thread, array)); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(jsArray); +} + +Local ObjectRef::GetOwnEnumerablePropertyNames(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle obj(JSNApiHelper::ToJSHandle(this)); + JSHandle array(JSObject::EnumerableOwnNames(thread, obj)); + JSHandle jsArray(JSArray::CreateArrayFromList(thread, array)); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(jsArray); +} + +Local ObjectRef::GetPrototype(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle object(JSNApiHelper::ToJSHandle(this)); + JSHandle prototype(thread, object->GetPrototype(thread)); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(prototype); +} + +bool ObjectRef::DefineProperty(const EcmaVM *vm, Local key, PropertyAttribute attribute) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle object(JSNApiHelper::ToJSHandle(this)); + JSHandle keyValue(JSNApiHelper::ToJSHandle(key)); + PropertyDescriptor desc(thread, attribute.IsWritable(), attribute.IsEnumerable(), attribute.IsConfigurable()); + desc.SetValue(JSNApiHelper::ToJSHandle(attribute.GetValue(vm))); + bool result = object->DefinePropertyOrThrow(thread, object, keyValue, desc); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +bool ObjectRef::Has(const EcmaVM *vm, Local key) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle object(JSNApiHelper::ToJSHandle(this)); + JSHandle keyValue(JSNApiHelper::ToJSHandle(key)); + bool result = object->HasProperty(thread, object, keyValue); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +bool ObjectRef::Has(const EcmaVM *vm, uint32_t key) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle object(JSNApiHelper::ToJSHandle(this)); + bool result = object->HasProperty(thread, object, key); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +bool ObjectRef::Delete(const EcmaVM *vm, Local key) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle object(JSNApiHelper::ToJSHandle(this)); + JSHandle keyValue(JSNApiHelper::ToJSHandle(key)); + bool result = object->DeleteProperty(thread, object, keyValue); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +bool ObjectRef::Delete(const EcmaVM *vm, uint32_t key) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle object(JSNApiHelper::ToJSHandle(this)); + JSHandle keyHandle(thread, JSTaggedValue(key)); + bool result = object->DeleteProperty(thread, object, keyHandle); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +// ----------------------------------- FunctionRef -------------------------------------- +Local FunctionRef::New(EcmaVM *vm, FunctionCallback nativeFunc, void *data) +{ + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle current(factory->NewJSFunction(env, reinterpret_cast(Callback::RegisterCallback))); + JSHandle funcCallback = factory->NewJSNativePointer(reinterpret_cast(nativeFunc)); + JSHandle dataCaddress = factory->NewJSNativePointer(data); + JSHandle extraInfo(factory->NewFunctionExtraInfo(funcCallback, dataCaddress)); + current->SetFunctionExtraInfo(thread, extraInfo.GetTaggedValue()); + return JSNApiHelper::ToLocal(JSHandle(current)); +} + +Local FunctionRef::NewClassFunction(EcmaVM *vm, FunctionCallbackWithNewTarget nativeFunc, void *data) +{ + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetFunctionClassWithoutName()); + JSMethod *method = + vm->GetMethodForNativeFunction(reinterpret_cast(Callback::RegisterCallbackWithNewTarget)); + JSHandle current = + factory->NewJSFunctionByDynClass(method, dynclass, ecmascript::FunctionKind::CLASS_CONSTRUCTOR); + JSHandle funcCallback = factory->NewJSNativePointer(reinterpret_cast(nativeFunc)); + JSHandle dataCaddress = factory->NewJSNativePointer(data); + JSHandle extraInfo(factory->NewFunctionExtraInfo(funcCallback, dataCaddress)); + current->SetFunctionExtraInfo(thread, extraInfo.GetTaggedValue()); + + JSHandle clsPrototype = + JSObject::ObjectCreate(thread, JSHandle(env->GetObjectFunctionPrototype())); + current->SetFunctionPrototype(thread, clsPrototype.GetTaggedValue()); + return JSNApiHelper::ToLocal(JSHandle(current)); +} + +Local FunctionRef::Call(const EcmaVM *vm, Local thisObj, + const Local argv[], // NOLINTNEXTLINE(modernize-avoid-c-arrays) + int32_t length) +{ + JSThread *thread = vm->GetJSThread(); + if (!IsFunction()) { + return JSValueRef::Undefined(vm); + } + JSHandle func = JSNApiHelper::ToJSHandle(this); + JSHandle thisValue = JSNApiHelper::ToJSHandle(thisObj); + ObjectFactory *factory = vm->GetFactory(); + JSHandle arguments = factory->NewTaggedArray(length); + Span> sp(argv, length); + for (int i = 0; i < length; ++i) { + arguments->Set(thread, i, JSNApiHelper::ToJSHandle(sp[i])); + } + JSTaggedValue result = JSFunction::Call(thread, func, thisValue, arguments); + RETURN_VALUE_IF_ABRUPT_NOT_CLEAR_EXCEPTION(thread, JSValueRef::Exception(vm)); + JSHandle resultValue(thread, result); + + vm->ExecutePromisePendingJob(); + RETURN_VALUE_IF_ABRUPT_NOT_CLEAR_EXCEPTION(thread, JSValueRef::Exception(vm)); + + return JSNApiHelper::ToLocal(resultValue); +} + +Local FunctionRef::Constructor(const EcmaVM *vm, + const Local argv[], // NOLINTNEXTLINE(modernize-avoid-c-arrays) + int32_t length) +{ + JSThread *thread = vm->GetJSThread(); + if (!IsFunction()) { + return JSValueRef::Undefined(vm); + } + JSHandle func = JSNApiHelper::ToJSHandle(this); + JSHandle newTarget = func; + ObjectFactory *factory = vm->GetFactory(); + JSHandle arguments = factory->NewTaggedArray(length); + Span> sp(argv, length); + for (int i = 0; i < length; ++i) { + arguments->Set(thread, i, JSNApiHelper::ToJSHandle(sp[i])); + } + JSTaggedValue result = JSFunction::Construct(thread, func, arguments, newTarget); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + JSHandle resultValue(vm->GetJSThread(), result); + return JSNApiHelper::ToLocal(resultValue); +} + +Local FunctionRef::GetFunctionPrototype(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle func = JSNApiHelper::ToJSHandle(this); + JSHandle prototype(thread, JSHandle(func)->GetFunctionPrototype()); + return JSNApiHelper::ToLocal(prototype); +} + +void FunctionRef::SetName(const EcmaVM *vm, Local name) +{ + JSThread *thread = vm->GetJSThread(); + JSFunction *func = JSFunction::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject()); + JSTaggedValue key = JSNApiHelper::ToJSTaggedValue(*name); + JSFunction::SetFunctionNameNoPrefix(thread, func, key); +} + +Local FunctionRef::GetName(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle func = JSHandle(thread, JSNApiHelper::ToJSTaggedValue(this)); + JSHandle name = JSFunctionBase::GetFunctionName(thread, func); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(name); +} + +bool FunctionRef::IsNative(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle func = JSHandle(thread, JSNApiHelper::ToJSTaggedValue(this)); + JSMethod *method = func->GetMethod(); + return method->IsNative(); +} + +// ----------------------------------- ArrayRef ---------------------------------------- +Local ArrayRef::New(const EcmaVM *vm, int32_t length) +{ + JSThread *thread = vm->GetJSThread(); + JSTaggedNumber arrayLen(length); + JSHandle array = JSArray::ArrayCreate(thread, arrayLen); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(array); +} + +int32_t ArrayRef::Length([[maybe_unused]] const EcmaVM *vm) +{ + return JSArray::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())->GetArrayLength(); +} + +// ---------------------------------- Promise -------------------------------------- +Local PromiseCapabilityRef::New(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle constructor(globalEnv->GetPromiseFunction()); + JSHandle capability(JSPromise::NewPromiseCapability(thread, constructor)); + return JSNApiHelper::ToLocal(capability); +} + +Local PromiseCapabilityRef::GetPromise(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle capacity(JSNApiHelper::ToJSHandle(this)); + return JSNApiHelper::ToLocal(JSHandle(thread, capacity->GetPromise())); +} + +bool PromiseCapabilityRef::Resolve(const EcmaVM *vm, Local value) +{ + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + const GlobalEnvConstants *constants = thread->GlobalConstants(); + + JSHandle arg = JSNApiHelper::ToJSHandle(value); + array_size_t length = 1; + JSHandle argv = factory->NewTaggedArray(length); + argv->Set(thread, 0, arg); + + JSHandle capacity(JSNApiHelper::ToJSHandle(this)); + JSHandle resolve(thread, capacity->GetResolve()); + JSHandle undefined(thread, constants->GetUndefined()); + JSFunction::Call(thread, resolve, undefined, argv); + RETURN_VALUE_IF_ABRUPT(thread, false); + return true; +} + +bool PromiseCapabilityRef::Reject(const EcmaVM *vm, Local reason) +{ + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + const GlobalEnvConstants *constants = thread->GlobalConstants(); + + JSHandle arg = JSNApiHelper::ToJSHandle(reason); + array_size_t length = 1; + JSHandle argv = factory->NewTaggedArray(length); + argv->Set(thread, 0, arg); + + JSHandle capacity(JSNApiHelper::ToJSHandle(this)); + JSHandle reject(thread, capacity->GetReject()); + JSHandle undefined(thread, constants->GetUndefined()); + JSFunction::Call(thread, reject, undefined, argv); + RETURN_VALUE_IF_ABRUPT(thread, false); + return true; +} + +Local PromiseRef::Catch(const EcmaVM *vm, Local handler) +{ + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + const GlobalEnvConstants *constants = thread->GlobalConstants(); + + JSHandle promise = JSNApiHelper::ToJSHandle(this); + JSHandle catchKey(thread, constants->GetPromiseCatchString()); + JSHandle reject = JSNApiHelper::ToJSHandle(handler); + JSHandle argv = factory->NewTaggedArray(1); + argv->Set(thread, 0, reject); + JSTaggedValue result = JSFunction::Invoke(thread, promise, catchKey, argv); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(JSHandle(thread, result)); +} + +Local PromiseRef::Then(const EcmaVM *vm, Local handler) +{ + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + const GlobalEnvConstants *constants = thread->GlobalConstants(); + + JSHandle promise = JSNApiHelper::ToJSHandle(this); + JSHandle thenKey(thread, constants->GetPromiseThenString()); + JSHandle resolver = JSNApiHelper::ToJSHandle(handler); + array_size_t length = 2; + JSHandle argv = factory->NewTaggedArray(length); + argv->Set(thread, 0, resolver); + argv->Set(thread, 1, constants->GetUndefined()); + JSTaggedValue result = JSFunction::Invoke(thread, promise, thenKey, argv); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(JSHandle(thread, result)); +} + +Local PromiseRef::Then(const EcmaVM *vm, Local onFulfilled, Local onRejected) +{ + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + const GlobalEnvConstants *constants = thread->GlobalConstants(); + + JSHandle promise = JSNApiHelper::ToJSHandle(this); + JSHandle thenKey(thread, constants->GetPromiseThenString()); + JSHandle resolver = JSNApiHelper::ToJSHandle(onFulfilled); + JSHandle reject = JSNApiHelper::ToJSHandle(onRejected); + array_size_t length = 2; + JSHandle argv = factory->NewTaggedArray(length); + argv->Set(thread, 0, resolver); + argv->Set(thread, 1, reject); + JSTaggedValue result = JSFunction::Invoke(thread, promise, thenKey, argv); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(JSHandle(thread, result)); +} +// ---------------------------------- Promise ------------------------------------- + +// ---------------------------------- Buffer ----------------------------------- +Local ArrayBufferRef::New(const EcmaVM *vm, int32_t length) +{ + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + + JSHandle arrayBuffer = factory->NewJSArrayBuffer(length); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(JSHandle(arrayBuffer)); +} + +Local ArrayBufferRef::New( + const EcmaVM *vm, void *buffer, int32_t length, const Deleter &deleter, void *data) +{ + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + + JSHandle arrayBuffer = + factory->NewJSArrayBuffer(buffer, length, reinterpret_cast(deleter), data); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(JSHandle(arrayBuffer)); +} + +int32_t ArrayBufferRef::ByteLength(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle arrayBuffer(JSNApiHelper::ToJSHandle(this)); + JSHandle length(thread, arrayBuffer->GetArrayBufferByteLength()); + if (!length->IsNumber()) { + return 0; + } + return length->GetNumber(); +} + +void *ArrayBufferRef::GetBuffer() +{ + JSHandle arrayBuffer(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue bufferData = arrayBuffer->GetArrayBufferData(); + if (!bufferData.IsJSNativePointer()) { + return nullptr; + } + return JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer(); +} +// ---------------------------------- Buffer ----------------------------------- + +// ---------------------------------- DataView ----------------------------------- +Local DataViewRef::New( + const EcmaVM *vm, Local arrayBuffer, int32_t byteOffset, int32_t byteLength) +{ + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + + JSHandle buffer(JSNApiHelper::ToJSHandle(arrayBuffer)); + JSHandle dataView = factory->NewJSDataView(buffer, byteOffset, byteLength); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(JSHandle(dataView)); +} + +int32_t DataViewRef::ByteLength() +{ + JSHandle dataView(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue length = dataView->GetByteLength(); + if (!length.IsNumber()) { + return 0; + } + return length.GetNumber(); +} + +int32_t DataViewRef::ByteOffset() +{ + JSHandle dataView(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue offset = dataView->GetByteOffset(); + if (!offset.IsNumber()) { + return 0; + } + return offset.GetNumber(); +} + +Local DataViewRef::GetArrayBuffer(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle dataView(JSNApiHelper::ToJSHandle(this)); + JSHandle arrayBuffer(thread, dataView->GetViewedArrayBuffer()); + return JSNApiHelper::ToLocal(arrayBuffer); +} +// ---------------------------------- DataView ----------------------------------- + +// ---------------------------------- TypedArray ----------------------------------- +int32_t TypedArrayRef::ByteLength(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle typeArray(JSNApiHelper::ToJSHandle(this)); + return TypedArrayHelper::GetByteLength(thread, typeArray); +} + +int32_t TypedArrayRef::ByteOffset(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle typeArray(JSNApiHelper::ToJSHandle(this)); + return TypedArrayHelper::GetByteOffset(thread, typeArray); +} + +int32_t TypedArrayRef::ArrayLength(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle typeArray(JSNApiHelper::ToJSHandle(this)); + return TypedArrayHelper::GetArrayLength(thread, typeArray); +} + +Local TypedArrayRef::GetArrayBuffer(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle typeArray(JSNApiHelper::ToJSHandle(this)); + JSHandle arrayBuffer(thread, TypedArrayHelper::GetViewedArrayBuffer(typeArray)); + return JSNApiHelper::ToLocal(arrayBuffer); +} + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TYPED_ARRAY_NEW(Type) \ + Local Type##Ref::New( \ + const EcmaVM *vm, Local buffer, int32_t byteOffset, int32_t length) \ + { \ + JSThread *thread = vm->GetJSThread(); \ + ObjectFactory *factory = vm->GetFactory(); \ + JSHandle env = vm->GetGlobalEnv(); \ + \ + JSHandle func = env->Get##Type##Function(); \ + JSHandle arrayBuffer(JSNApiHelper::ToJSHandle(buffer)); \ + array_size_t arrayLength = 3; \ + JSHandle argv = factory->NewTaggedArray(arrayLength); \ + argv->Set(thread, 0, arrayBuffer); \ + argv->Set(thread, 1, JSTaggedValue(byteOffset)); \ + argv->Set(thread, 2, JSTaggedValue(length)); \ + JSTaggedValue result = JSFunction::Construct(thread, func, argv, func); \ + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); \ + JSHandle resultHandle(thread, result); \ + return JSNApiHelper::ToLocal(resultHandle); \ + } + +TYPED_ARRAY_ALL(TYPED_ARRAY_NEW) + +#undef TYPED_ARRAY_NEW +// ---------------------------------- TypedArray ----------------------------------- + +// ---------------------------------- Error --------------------------------------- +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define EXCEPTION_ERROR_NEW(name, type) \ + Local Exception::name(const EcmaVM *vm, Local message) \ + { \ + JSThread *thread = vm->GetJSThread(); \ + ObjectFactory *factory = vm->GetFactory(); \ + \ + JSHandle messageValue(JSNApiHelper::ToJSHandle(message)); \ + JSHandle result(factory->NewJSError(ErrorType::type, messageValue)); \ + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); \ + return JSNApiHelper::ToLocal(result); \ + } + +EXCEPTION_ERROR_ALL(EXCEPTION_ERROR_NEW) + +#undef EXCEPTION_ERROR_NEW +// ---------------------------------- Error --------------------------------------- + +// ---------------------------------- JSON ------------------------------------------ +Local JSON::Parse(const EcmaVM *vm, Local string) +{ + JSThread *thread = vm->GetJSThread(); + JsonParser parser(thread); + JSHandle result = + parser.Parse(EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(*string).GetTaggedObject())); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(result); +} + +Local JSON::Stringify(const EcmaVM *vm, Local json) +{ + JSThread *thread = vm->GetJSThread(); + auto constants = thread->GlobalConstants(); + JsonStringifier stringifier(thread); + JSHandle str = stringifier.Stringify( + JSNApiHelper::ToJSHandle(json), constants->GetHandledUndefined(), constants->GetHandledUndefined()); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(str); +} + +Local RegExpRef::GetOriginalSource(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle regExp(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue source = regExp->GetOriginalSource(); + if (!source.IsString()) { + auto constants = thread->GlobalConstants(); + return JSNApiHelper::ToLocal(constants->GetHandledEmptyString()); + } + JSHandle sourceHandle(thread, source); + return JSNApiHelper::ToLocal(sourceHandle); +} + +Local DateRef::ToString(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle date(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue dateStr = date->ToString(thread); + if (!dateStr.IsString()) { + auto constants = thread->GlobalConstants(); + return JSNApiHelper::ToLocal(constants->GetHandledEmptyString()); + } + JSHandle dateStrHandle(thread, dateStr); + return JSNApiHelper::ToLocal(dateStrHandle); +} + +int32_t MapRef::GetSize() +{ + JSHandle map(JSNApiHelper::ToJSHandle(this)); + return map->GetSize(); +} + +int32_t SetRef::GetSize() +{ + JSHandle set(JSNApiHelper::ToJSHandle(this)); + return set->GetSize(); +} + +// ----------------------------------- FunctionCallback --------------------------------- +JSTaggedValue Callback::RegisterCallback(ecmascript::EcmaRuntimeCallInfo *info) +{ + // Constructor + JSThread *thread = info->GetThread(); + JSHandle constructor = BuiltinsBase::GetConstructor(info); + if (!constructor->IsJSFunction()) { + return JSTaggedValue::False(); + } + JSHandle function(constructor); + JSHandle extraInfoValue(thread, function->GetFunctionExtraInfo()); + if (!extraInfoValue->IsJSFunctionExtraInfo()) { + return JSTaggedValue::False(); + } + JSHandle extraInfo(extraInfoValue); + // vm + Region *region = Region::ObjectAddressToRange(extraInfo.GetTaggedValue().GetTaggedObject()); + if (region == nullptr) { + return JSTaggedValue::False(); + } + EcmaVM *vm = region->GetSpace()->GetHeap()->GetEcmaVM(); + // data + JSHandle data(thread, extraInfo->GetData()); + if (!data->IsHeapObject()) { + return JSTaggedValue::False(); + } + JSHandle dataObj(data); + // callBack + JSHandle callBack(thread, extraInfo->GetCallback()); + if (!callBack->IsHeapObject()) { + return JSTaggedValue::False(); + } + JSHandle callBackObj(callBack); + FunctionCallback nativeFunc = (reinterpret_cast(callBackObj->GetExternalPointer())); + + // this + JSHandle thisValue(BuiltinsBase::GetThis(info)); + + // arguments + std::vector> arguments; + array_size_t length = info->GetArgsNumber(); + for (array_size_t i = 0; i < length; ++i) { + arguments.emplace_back(JSNApiHelper::ToLocal(BuiltinsBase::GetCallArg(info, i))); + } + + Local result = nativeFunc(vm, + JSNApiHelper::ToLocal(thisValue), + arguments.data(), + arguments.size(), + dataObj->GetExternalPointer()); + return JSNApiHelper::ToJSHandle(result).GetTaggedValue(); +} + +JSTaggedValue Callback::RegisterCallbackWithNewTarget(ecmascript::EcmaRuntimeCallInfo *info) +{ + // Constructor + JSThread *thread = info->GetThread(); + JSHandle constructor = BuiltinsBase::GetConstructor(info); + if (!constructor->IsJSFunction()) { + return JSTaggedValue::False(); + } + JSHandle function(constructor); + JSHandle extraInfoValue(thread, function->GetFunctionExtraInfo()); + if (!extraInfoValue->IsJSFunctionExtraInfo()) { + return JSTaggedValue::False(); + } + JSHandle extraInfo(extraInfoValue); + // vm + Region *region = Region::ObjectAddressToRange(extraInfo.GetTaggedValue().GetTaggedObject()); + if (region == nullptr) { + return JSTaggedValue::False(); + } + EcmaVM *vm = region->GetSpace()->GetHeap()->GetEcmaVM(); + // data + JSHandle data(thread, extraInfo->GetData()); + if (!data->IsHeapObject()) { + return JSTaggedValue::False(); + } + JSHandle dataObj(data); + // callBack + JSHandle callBack(thread, extraInfo->GetCallback()); + if (!callBack->IsHeapObject()) { + return JSTaggedValue::False(); + } + JSHandle callBackObj(callBack); + FunctionCallbackWithNewTarget nativeFunc = + (reinterpret_cast(callBackObj->GetExternalPointer())); + + // newTarget + JSHandle newTarget(BuiltinsBase::GetConstructor(info)); + + // this + JSHandle thisValue(BuiltinsBase::GetThis(info)); + + // arguments + std::vector> arguments; + array_size_t length = info->GetArgsNumber(); + for (array_size_t i = 0; i < length; ++i) { + arguments.emplace_back(JSNApiHelper::ToLocal(BuiltinsBase::GetCallArg(info, i))); + } + + Local result = nativeFunc(vm, + JSNApiHelper::ToLocal(thisValue), + JSNApiHelper::ToLocal(newTarget), + arguments.data(), + arguments.size(), + dataObj->GetExternalPointer()); + return JSNApiHelper::ToJSHandle(result).GetTaggedValue(); +} + +// ------------------------------------- JSExecutionScope ------------------------------ +JSExecutionScope::JSExecutionScope(const EcmaVM *vm) +{ + (void)vm; +} + +JSExecutionScope::~JSExecutionScope() +{ + last_current_thread_ = nullptr; + is_revert_ = false; +} + +// ----------------------------------- JSValueRef -------------------------------------- +Local JSValueRef::Undefined(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal(JSHandle(vm->GetJSThread(), JSTaggedValue::Undefined())); +} + +Local JSValueRef::Null(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal(JSHandle(vm->GetJSThread(), JSTaggedValue::Null())); +} + +Local JSValueRef::True(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal(JSHandle(vm->GetJSThread(), JSTaggedValue::True())); +} + +Local JSValueRef::False(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal(JSHandle(vm->GetJSThread(), JSTaggedValue::False())); +} + +Local JSValueRef::Exception(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal(JSHandle(vm->GetJSThread(), JSTaggedValue::Exception())); +} + +Local JSValueRef::ToObject(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + if (IsUndefined() || IsNull()) { + return Exception(vm); + } + JSHandle obj(JSTaggedValue::ToObject(thread, JSNApiHelper::ToJSHandle(this))); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(obj); +} + +Local JSValueRef::ToString(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + if (!obj->IsString()) { + obj = JSHandle(JSTaggedValue::ToString(thread, obj)); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + } + return JSNApiHelper::ToLocal(obj); +} + +bool JSValueRef::BooleaValue() +{ + return JSNApiHelper::ToJSTaggedValue(this).ToBoolean(); +} + +int64_t JSValueRef::IntegerValue(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSTaggedNumber number = JSTaggedValue::ToInteger(thread, JSNApiHelper::ToJSHandle(this)); + RETURN_VALUE_IF_ABRUPT(thread, 0); + return number.GetNumber(); +} + +uint32_t JSValueRef::Uint32Value(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + uint32_t number = JSTaggedValue::ToUint32(thread, JSNApiHelper::ToJSHandle(this)); + RETURN_VALUE_IF_ABRUPT(thread, 0); + return number; +} + +int32_t JSValueRef::Int32Value(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + int32_t number = JSTaggedValue::ToInt32(thread, JSNApiHelper::ToJSHandle(this)); + RETURN_VALUE_IF_ABRUPT(thread, 0); + return number; +} + +Local JSValueRef::ToBoolean(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + JSHandle booleanObj = JSHandle(thread, JSTaggedValue(obj->ToBoolean())); + return JSNApiHelper::ToLocal(booleanObj); +} + +Local JSValueRef::ToNumber(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + JSHandle number(thread, JSTaggedValue::ToNumber(thread, obj)); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(number); +} + +bool JSValueRef::IsStrictEquals(const EcmaVM *vm, Local value) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle xValue = JSNApiHelper::ToJSHandle(this); + JSHandle yValue = JSNApiHelper::ToJSHandle(value); + return JSTaggedValue::StrictEqual(thread, xValue, yValue); +} + +Local JSValueRef::Typeof(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSTaggedValue value = FastRuntimeStub::FastTypeOf(thread, JSNApiHelper::ToJSTaggedValue(this)); + return JSNApiHelper::ToLocal(JSHandle(thread, value)); +} + +bool JSValueRef::InstanceOf(const EcmaVM *vm, Local value) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle origin = JSNApiHelper::ToJSHandle(this); + JSHandle target = JSNApiHelper::ToJSHandle(value); + bool result = JSObject::InstanceOf(thread, origin, target); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +bool JSValueRef::IsUndefined() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsUndefined(); +} + +bool JSValueRef::IsNull() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsNull(); +} + +bool JSValueRef::IsHole() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsHole(); +} + +bool JSValueRef::IsTrue() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsTrue(); +} + +bool JSValueRef::IsFalse() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsFalse(); +} + +bool JSValueRef::IsNumber() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsNumber(); +} + +bool JSValueRef::IsInt() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsInt(); +} + +bool JSValueRef::WithinInt32() +{ + return JSNApiHelper::ToJSTaggedValue(this).WithinInt32(); +} + +bool JSValueRef::IsBoolean() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsBoolean(); +} + +bool JSValueRef::IsString() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsString(); +} + +bool JSValueRef::IsSymbol() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsSymbol(); +} + +bool JSValueRef::IsObject() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsECMAObject(); +} + +bool JSValueRef::IsArray(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + return JSNApiHelper::ToJSTaggedValue(this).IsArray(thread); +} + +bool JSValueRef::IsConstructor() +{ + JSTaggedValue value = JSNApiHelper::ToJSTaggedValue(this); + return value.IsHeapObject() && value.IsConstructor(); +} + +bool JSValueRef::IsFunction() +{ + JSTaggedValue value = JSNApiHelper::ToJSTaggedValue(this); + return value.IsHeapObject() && value.IsCallable(); +} + +bool JSValueRef::IsProxy() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSProxy(); +} + +bool JSValueRef::IsException() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsException(); +} + +bool JSValueRef::IsPromise() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSPromise(); +} + +bool JSValueRef::IsDataView() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsDataView(); +} + +bool JSValueRef::IsTypedArray() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsTypedArray(); +} + +bool JSValueRef::IsNativePointer() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSNativePointer(); +} + +bool JSValueRef::IsNativeObject() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSNativeObject(); +} + +bool JSValueRef::IsDate() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsDate(); +} + +bool JSValueRef::IsError() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSError(); +} + +bool JSValueRef::IsMap() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSMap(); +} + +bool JSValueRef::IsSet() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSSet(); +} + +bool JSValueRef::IsWeakMap() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSWeakMap(); +} + +bool JSValueRef::IsWeakSet() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSWeakSet(); +} + +bool JSValueRef::IsRegExp() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSRegExp(); +} + +bool JSValueRef::IsArrayIterator() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSArrayIterator(); +} + +bool JSValueRef::IsStringIterator() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsStringIterator(); +} + +bool JSValueRef::IsSetIterator() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSSetIterator(); +} + +bool JSValueRef::IsMapIterator() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSMapIterator(); +} + +bool JSValueRef::IsArrayBuffer() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsArrayBuffer(); +} + +bool JSValueRef::IsUint8Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSUint8Array(); +} + +bool JSValueRef::IsInt8Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSInt8Array(); +} + +bool JSValueRef::IsUint8ClampedArray() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSUint8ClampedArray(); +} + +bool JSValueRef::IsInt16Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSInt16Array(); +} + +bool JSValueRef::IsUint16Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSUint16Array(); +} + +bool JSValueRef::IsInt32Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSInt32Array(); +} + +bool JSValueRef::IsUint32Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSUint32Array(); +} + +bool JSValueRef::IsFloat32Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSFloat32Array(); +} + +bool JSValueRef::IsFloat64Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSFloat64Array(); +} +} // namespace panda diff --git a/ecmascript/napi/jsnapi_helper-inl.h b/ecmascript/napi/jsnapi_helper-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..6c945b26b30c5fb66d1a8485100036e2859a0265 --- /dev/null +++ b/ecmascript/napi/jsnapi_helper-inl.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_NAPI_JSNAPI_HELPER_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_NAPI_JSNAPI_HELPER_INL_H + +#include "ecmascript/js_tagged_value.h" +#include "jsnapi_helper.h" +#include "libpandabase/macros.h" + +namespace panda { +template +Local JSNApiHelper::ToLocal(ecmascript::JSHandle from) +{ + return Local(from.GetAddress()); +} + +ecmascript::JSTaggedValue JSNApiHelper::ToJSTaggedValue(JSValueRef *from) +{ + ASSERT(from != nullptr); + return *reinterpret_cast(from); +} + +ecmascript::JSHandle JSNApiHelper::ToJSHandle(Local from) +{ + ASSERT(!from.IsEmpty()); + return ecmascript::JSHandle(reinterpret_cast(*from)); +} + +ecmascript::JSHandle JSNApiHelper::ToJSHandle(JSValueRef *from) +{ + ASSERT(from != nullptr); + return ecmascript::JSHandle(reinterpret_cast(from)); +} +} // namespace panda +#endif // PANDA_RUNTIME_ECMASCRIPT_NAPI_JSNAPI_HELPER_INL_H \ No newline at end of file diff --git a/ecmascript/napi/jsnapi_helper.h b/ecmascript/napi/jsnapi_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..1b0c67ec935b2effdfac3aa9fc096928bb3c4e99 --- /dev/null +++ b/ecmascript/napi/jsnapi_helper.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_NAPI_JSNAPI_HELPER_H +#define PANDA_RUNTIME_ECMASCRIPT_NAPI_JSNAPI_HELPER_H + +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/napi/include/jsnapi.h" + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_VALUE_IF_ABRUPT(thread, value) \ + do { \ + if (thread->HasPendingException()) { \ + thread->ClearException(); \ + return value; \ + } \ + } while (false) + +#define RETURN_VALUE_IF_ABRUPT_NOT_CLEAR_EXCEPTION(thread, value) \ + do { \ + if (thread->HasPendingException()) { \ + return value; \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TYPED_ARRAY_ALL(V) \ + V(Int8Array) \ + V(Uint8Array) \ + V(Uint8ClampedArray) \ + V(Int16Array) \ + V(Uint16Array) \ + V(Int32Array) \ + V(Uint32Array) \ + V(Float32Array) \ + V(Float64Array) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define EXCEPTION_ERROR_ALL(V) \ + V(Error, ERROR) \ + V(RangeError, RANGE_ERROR) \ + V(SyntaxError, SYNTAX_ERROR) \ + V(ReferenceError, REFERENCE_ERROR) \ + V(TypeError, TYPE_ERROR) \ + V(EvalError, EVAL_ERROR) + +namespace panda { +class JSNApiHelper { +public: + template + static inline Local ToLocal(ecmascript::JSHandle from); + + static inline ecmascript::JSTaggedValue ToJSTaggedValue(JSValueRef *from); + + static inline ecmascript::JSHandle ToJSHandle(Local from); + + static inline ecmascript::JSHandle ToJSHandle(JSValueRef *from); +}; + +class Callback { +public: + static ecmascript::JSTaggedValue RegisterCallback(ecmascript::EcmaRuntimeCallInfo *info); + static ecmascript::JSTaggedValue RegisterCallbackWithNewTarget(ecmascript::EcmaRuntimeCallInfo *info); +}; +} // namespace panda +#endif // PANDA_RUNTIME_ECMASCRIPT_JSNAPI_JSNAPI_HELPER_H diff --git a/ecmascript/napi/test/BUILD.gn b/ecmascript/napi/test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..82781d2c44a00a48ef36ec33ee62e7fde7d1edd3 --- /dev/null +++ b/ecmascript/napi/test/BUILD.gn @@ -0,0 +1,50 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//ark/js_runtime/test/test_helper.gni") +import("//build/test.gni") + +module_output_path = "ark/js_runtime" + +host_unittest_action("JsnapiTest") { + module_out_path = module_output_path + + sources = [ + # test file + "jsnapi_tests.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +group("unittest") { + testonly = true + deps = [ ":JsnapiTest" ] +} + +group("host_unittest") { + testonly = true + deps = [ ":JsnapiTestAction(${host_toolchain})" ] +} diff --git a/ecmascript/napi/test/jsi_test.cpp b/ecmascript/napi/test/jsi_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..07a4784b0d290c54660e93a50d4438ea78fbcc37 --- /dev/null +++ b/ecmascript/napi/test/jsi_test.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +// those head files is in ace_engine +#include "ark_js_runtime.h" +#include "js_value.h" + +using OHOS::Ace::Framework::ArkJSRuntime; +using OHOS::Ace::Framework::JsRuntime; +using OHOS::Ace::Framework::JsValue; +using OHOS::Ace::Framework::RegisterFunctionType; +using std::shared_ptr; + +std::string GetLogContent(const shared_ptr &runtime, const std::vector> &argument) +{ + std::string context; + for (const auto &value : argument) { + context += value->ToString(runtime); + } + return context; +} + +shared_ptr AppDebugLogPrint(const shared_ptr &runtime, const shared_ptr &, + const std::vector> &argument, int32_t) +{ + std::string context = GetLogContent(runtime, argument); + std::cout << context.c_str() << std::endl; + return runtime->NewObject(); +} + +shared_ptr Setter(const shared_ptr &runtime, const shared_ptr &, + const std::vector> &argument, int32_t) +{ + std::string context = GetLogContent(runtime, argument); + std::cout << context.c_str() << std::endl; + return runtime->NewObject(); +} + +shared_ptr Getter(const shared_ptr &runtime, const shared_ptr &, + const std::vector> &argument, int32_t) +{ + std::string context = GetLogContent(runtime, argument); + std::cout << "Getter" << std::endl; + return runtime->NewObject(); +} + +int PrintLog([[maybe_unused]] int id, [[maybe_unused]] int level, const char *tag, [[maybe_unused]] const char *fmt, + const char *message) +{ + std::cout << tag << "::" << message; + return 0; +} + +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) +class TestRuntime { +public: + TestRuntime() + { + runtime_ = ArkJSRuntime::GetInstance(); + runtime_->SetLogPrint(PrintLog); + runtime_->Initialize(""); + } + ~TestRuntime() + { + runtime_->Reset(); + } + inline const shared_ptr &operator*() const + { + return runtime_; + } + inline const shared_ptr &operator->() const + { + return runtime_; + } + +private: + shared_ptr runtime_; +}; + +int main() +{ + TestRuntime runtime; + RegisterFunctionType func = AppDebugLogPrint; + shared_ptr global = runtime->GetGlobal(); + shared_ptr logFunc = runtime->NewFunction(func); + shared_ptr consoleObj = runtime->NewObject(); + global->SetProperty(*runtime, "console", consoleObj); + consoleObj->SetProperty(*runtime, "log", logFunc); + consoleObj->SetProperty(*runtime, "info", logFunc); + + shared_ptr getSetTest = runtime->NewObject(); + shared_ptr setter = runtime->NewFunction(Setter); + shared_ptr getter = runtime->NewFunction(Getter); + bool getset = getSetTest->SetAccessorProperty(*runtime, "GetSetTest", getter, setter); + std::cout << "SetAccessorProperty result: " << getset << std::endl; + global->SetProperty(*runtime, "GetSet", getSetTest); + + std::vector> arguments; + arguments.emplace_back(runtime->NewString("Hello world")); + + consoleObj = global->GetProperty(*runtime, "console"); + logFunc = consoleObj->GetProperty(*runtime, "log"); + shared_ptr testObj = logFunc->Call(*runtime, runtime->NewUndefined(), arguments, 1); + + shared_ptr testArrayValue = runtime->NewString("1"); + shared_ptr testValue = runtime->NewArray(); + testValue->SetProperty(*runtime, "0", testArrayValue); + testValue->SetProperty(*runtime, "1", testArrayValue); + std::cout << "GetProperty test: " << testValue->GetArrayLength(*runtime) << std::endl; + + testObj->SetProperty(*runtime, "test", testValue); + + shared_ptr result = testObj->GetProperty(*runtime, "test"); + std::cout << "GetProperty test: " << result->IsArray(*runtime) << ";" << result->GetArrayLength(*runtime) + << std::endl; + + std::vector argument; + runtime->ExecuteJsBin("native.aex"); + + return 0; +} diff --git a/ecmascript/napi/test/jsnapi_tests.cpp b/ecmascript/napi/test/jsnapi_tests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3cb9bcddcd5a3f5896f1c90d74d52cd76d82752e --- /dev/null +++ b/ecmascript/napi/test/jsnapi_tests.cpp @@ -0,0 +1,606 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tests/test_helper.h" + +#include + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/napi/include/jsnapi.h" +#include "ecmascript/object_factory.h" + +using namespace panda; +using namespace panda::ecmascript; + +namespace panda::test { +class JSNApiTests : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + RuntimeOption option; + option.SetLogLevel(RuntimeOption::LOG_LEVEL::ERROR); + vm_ = JSNApi::CreateJSVM(option); + ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime"; + thread_ = vm_->GetJSThread(); + thread_->SetIsEcmaInterpreter(true); + vm_->GetFactory()->SetTriggerGc(true); + } + + void TearDown() override + { + vm_->GetFactory()->SetTriggerGc(false); + JSNApi::DestoryJSVM(vm_); + } + +protected: + JSThread *thread_ = nullptr; + EcmaVM *vm_ = nullptr; +}; + +Local FunctionCallback(EcmaVM *vm, Local, const Local *, int32_t length, void *) +{ + EscapeLocalScope scope(vm); + return scope.Escape(ArrayRef::New(vm, length)); +} + +HWTEST_F_L0(JSNApiTests, GetGlobalObject) +{ + LocalScope scope(vm_); + Local globalObject = JSNApi::GetGlobalObject(vm_); + ASSERT_FALSE(globalObject.IsEmpty()); + ASSERT_TRUE(globalObject->IsObject()); +} + +HWTEST_F_L0(JSNApiTests, RegisterFunction) +{ + LocalScope scope(vm_); + Local callback = FunctionRef::New(vm_, FunctionCallback, nullptr); + ASSERT_TRUE(!callback.IsEmpty()); + std::vector> arguments; + arguments.emplace_back(JSValueRef::Undefined(vm_)); + Local result = + callback->Call(vm_, JSValueRef::Undefined(vm_), arguments.data(), arguments.size()); + ASSERT_TRUE(result->IsArray(vm_)); + Local array(result); + ASSERT_EQ(array->Length(vm_), arguments.size()); +} + +HWTEST_F_L0(JSNApiTests, GetProperty) +{ + LocalScope scope(vm_); + Local globalObject = JSNApi::GetGlobalObject(vm_); + ASSERT_FALSE(globalObject.IsEmpty()); + ASSERT_TRUE(globalObject->IsObject()); + + Local key = StringRef::NewFromUtf8(vm_, "Number"); + Local property = globalObject->Get(vm_, key); + ASSERT_TRUE(property->IsFunction()); +} + +HWTEST_F_L0(JSNApiTests, SetProperty) +{ + LocalScope scope(vm_); + Local globalObject = JSNApi::GetGlobalObject(vm_); + ASSERT_FALSE(globalObject.IsEmpty()); + ASSERT_TRUE(globalObject->IsObject()); + + Local property = ArrayRef::New(vm_, 3); // 3 : length + ASSERT_TRUE(property->IsArray(vm_)); + ASSERT_EQ(property->Length(vm_), 3); // 3 : test case of input + + Local key = StringRef::NewFromUtf8(vm_, "Test"); + bool result = globalObject->Set(vm_, key, property); + ASSERT_TRUE(result); + + Local propertyGet = globalObject->Get(vm_, key); + ASSERT_TRUE(propertyGet->IsArray(vm_)); + ASSERT_EQ(Local(propertyGet)->Length(vm_), 3); // 3 : test case of input +} + +HWTEST_F_L0(JSNApiTests, JsonParser) +{ + LocalScope scope(vm_); + Local globalObject = JSNApi::GetGlobalObject(vm_); + ASSERT_FALSE(globalObject.IsEmpty()); + ASSERT_TRUE(globalObject->IsObject()); + + const char *const test{R"({"orientation": "portrait"})"}; + Local jsonString = StringRef::NewFromUtf8(vm_, test); + + Local result = JSON::Parse(vm_, jsonString); + ASSERT_TRUE(result->IsObject()); + + Local keyString = StringRef::NewFromUtf8(vm_, "orientation"); + Local property = Local(result)->Get(vm_, keyString); + ASSERT_TRUE(property->IsString()); +} + +HWTEST_F_L0(JSNApiTests, StrictEqual) +{ + LocalScope scope(vm_); + Local origin = StringRef::NewFromUtf8(vm_, "1"); + Local target1 = StringRef::NewFromUtf8(vm_, "1"); + Local target = NumberRef::New(vm_, 1); + + ASSERT_FALSE(origin->IsStrictEquals(vm_, target)); + ASSERT_TRUE(origin->IsStrictEquals(vm_, target1)); +} + +HWTEST_F_L0(JSNApiTests, InstanceOf) +{ + LocalScope scope(vm_); + Local target = FunctionRef::New(vm_, nullptr, nullptr); + Local origin = ArrayRef::New(vm_, 1); + + ASSERT_FALSE(origin->InstanceOf(vm_, target)); +} + +HWTEST_F_L0(JSNApiTests, TypeOf) +{ + LocalScope scope(vm_); + Local origin = StringRef::NewFromUtf8(vm_, "1"); + Local typeString = origin->Typeof(vm_); + ASSERT_EQ(typeString->ToString(), "string"); + + Local target = NumberRef::New(vm_, 1); + typeString = target->Typeof(vm_); + ASSERT_EQ(typeString->ToString(), "number"); +} + +HWTEST_F_L0(JSNApiTests, Symbol) +{ + LocalScope scope(vm_); + Local description = StringRef::NewFromUtf8(vm_, "test"); + Local symbol = SymbolRef::New(vm_, description); + + ASSERT_FALSE(description->IsSymbol()); + ASSERT_TRUE(symbol->IsSymbol()); +} + +HWTEST_F_L0(JSNApiTests, StringUtf8) +{ + LocalScope scope(vm_); + std::string test = "Hello world"; + Local testString = StringRef::NewFromUtf8(vm_, test.c_str()); + + ASSERT_TRUE(testString->Utf8Length() == 12); // 12 : length of testString("Hello World") + char buffer[12]; // 12 : length of testString + ASSERT_TRUE(testString->WriteUtf8(buffer, 12) == 12); // 12 : length of testString("Hello World") + std::string res(buffer); + ASSERT_EQ(res, test); +} + +HWTEST_F_L0(JSNApiTests, ToType) +{ + LocalScope scope(vm_); + Local toString = StringRef::NewFromUtf8(vm_, "-123.3"); + Local toValue(toString); + + ASSERT_EQ(toString->ToNumber(vm_)->Value(), -123.3); // -123 : test case of input + ASSERT_EQ(toString->ToBoolean(vm_)->Value(), true); + ASSERT_EQ(toValue->ToString(vm_)->ToString(), "-123.3"); + ASSERT_TRUE(toValue->ToObject(vm_)->IsObject()); +} + +HWTEST_F_L0(JSNApiTests, TypeValue) +{ + LocalScope scope(vm_); + Local toString = StringRef::NewFromUtf8(vm_, "-123"); + Local toValue(toString); + + ASSERT_EQ(toString->Int32Value(vm_), -123); // -123 : test case of input + ASSERT_EQ(toString->BooleaValue(), true); + ASSERT_EQ(toString->Uint32Value(vm_), 4294967173); // 4294967173 : test case of input + ASSERT_EQ(toString->IntegerValue(vm_), -123); // -123 : test case of input +} + +HWTEST_F_L0(JSNApiTests, DefineProperty) +{ + LocalScope scope(vm_); + Local object = ObjectRef::New(vm_); + Local key = StringRef::NewFromUtf8(vm_, "TestKey"); + Local value = ObjectRef::New(vm_); + PropertyAttribute attribute(value, true, true, true); + + ASSERT_TRUE(object->DefineProperty(vm_, key, attribute)); + Local value1 = object->Get(vm_, key); + ASSERT_TRUE(value->IsStrictEquals(vm_, value1)); +} + +HWTEST_F_L0(JSNApiTests, HasProperty) +{ + LocalScope scope(vm_); + Local object = ObjectRef::New(vm_); + Local key = StringRef::NewFromUtf8(vm_, "TestKey"); + Local value = ObjectRef::New(vm_); + PropertyAttribute attribute(value, true, true, true); + + ASSERT_TRUE(object->DefineProperty(vm_, key, attribute)); + ASSERT_TRUE(object->Has(vm_, key)); +} + +HWTEST_F_L0(JSNApiTests, DeleteProperty) +{ + LocalScope scope(vm_); + Local object = ObjectRef::New(vm_); + Local key = StringRef::NewFromUtf8(vm_, "TestKey"); + Local value = ObjectRef::New(vm_); + PropertyAttribute attribute(value, true, true, true); + + ASSERT_TRUE(object->DefineProperty(vm_, key, attribute)); + ASSERT_TRUE(object->Delete(vm_, key)); + ASSERT_FALSE(object->Has(vm_, key)); +} + +HWTEST_F_L0(JSNApiTests, GetProtoType) +{ + LocalScope scope(vm_); + Local function = FunctionRef::New(vm_, nullptr, nullptr); + Local protoType = function->GetPrototype(vm_); + ASSERT_TRUE(protoType->IsObject()); + + Local object = ObjectRef::New(vm_); + protoType = object->GetPrototype(vm_); + ASSERT_TRUE(protoType->IsObject()); +} + +void CheckReject(EcmaVM *, Local, const Local argv[], int32_t length, void *) +{ + ASSERT_EQ(length, 1); + Local reason = argv[0]; + ASSERT_TRUE(reason->IsString()); + ASSERT_EQ(Local(reason)->ToString(), "Reject"); +} + +Local RejectCallback(EcmaVM *vm, Local thisArg, const Local argv[], int32_t length, + void *data) +{ + LocalScope scope(vm); + CheckReject(vm, thisArg, argv, length, data); + return JSValueRef::Undefined(vm); +} + +HWTEST_F_L0(JSNApiTests, PromiseCatch) +{ + LocalScope scope(vm_); + Local capability = PromiseCapabilityRef::New(vm_); + + Local promise = capability->GetPromise(vm_); + Local reject = FunctionRef::New(vm_, RejectCallback, nullptr); + Local catchPromise = promise->Catch(vm_, reject); + ASSERT_TRUE(promise->IsPromise()); + ASSERT_TRUE(catchPromise->IsPromise()); + + Local reason = StringRef::NewFromUtf8(vm_, "Reject"); + ASSERT_TRUE(capability->Reject(vm_, reason)); + + vm_->ExecutePromisePendingJob(); +} + +void CheckResolve(EcmaVM *, Local, const Local argv[], int32_t length, void *) +{ + ASSERT_EQ(length, 1); + Local value = argv[0]; + ASSERT_TRUE(value->IsNumber()); + ASSERT_EQ(Local(value)->Value(), 300.3); // 300.3 : test case of input +} + +Local ResolvedCallback(EcmaVM *vm, Local thisArg, const Local argv[], + int32_t length, void *data) +{ + LocalScope scope(vm); + CheckResolve(vm, thisArg, argv, length, data); + return JSValueRef::Undefined(vm); +} + +HWTEST_F_L0(JSNApiTests, PromiseThen) +{ + LocalScope scope(vm_); + Local capability = PromiseCapabilityRef::New(vm_); + + Local promise = capability->GetPromise(vm_); + Local resolve = FunctionRef::New(vm_, ResolvedCallback, nullptr); + Local reject = FunctionRef::New(vm_, RejectCallback, nullptr); + Local thenPromise = promise->Then(vm_, resolve, reject); + ASSERT_TRUE(promise->IsPromise()); + ASSERT_TRUE(thenPromise->IsPromise()); + + Local value = NumberRef::New(vm_, 300.3); // 300.3 : test case of input + ASSERT_TRUE(capability->Resolve(vm_, value)); + vm_->ExecutePromisePendingJob(); +} + +HWTEST_F_L0(JSNApiTests, Constructor) +{ + LocalScope scope(vm_); + Local object = JSNApi::GetGlobalObject(vm_); + Local key = StringRef::NewFromUtf8(vm_, "Number"); + Local numberConstructor = object->Get(vm_, key); + Local argv[1]; + argv[0] = NumberRef::New(vm_, 1.3); // 1.3 : test case of input + Local result = numberConstructor->Constructor(vm_, argv, 1); + ASSERT_TRUE(result->IsObject()); + ASSERT_EQ(result->ToNumber(vm_)->Value(), 1.3); // 1.3 : size of arguments +} + +HWTEST_F_L0(JSNApiTests, ArrayBuffer) +{ + LocalScope scope(vm_); + const int32_t length = 15; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + ASSERT_EQ(arrayBuffer->ByteLength(vm_), length); + ASSERT_NE(arrayBuffer->GetBuffer(), nullptr); + JSNApi::TriggerGC(vm_); +} + +HWTEST_F_L0(JSNApiTests, ArrayBufferWithBuffer) +{ + static bool isFree = false; + struct Data { + int32_t length; + }; + const int32_t length = 15; + Data *data = new Data(); + data->length = length; + Deleter deleter = [](void *buffer, void *data) -> void { + delete[] reinterpret_cast(buffer); + Data *currentData = reinterpret_cast(data); + ASSERT_EQ(currentData->length, 15); // 5 : size of arguments + delete currentData; + isFree = true; + }; + { + LocalScope scope(vm_); + uint8_t *buffer = new uint8_t[length]; + Local arrayBuffer = ArrayBufferRef::New(vm_, buffer, length, deleter, data); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + ASSERT_EQ(arrayBuffer->ByteLength(vm_), length); + ASSERT_EQ(arrayBuffer->GetBuffer(), buffer); + } + JSNApi::TriggerGC(vm_); + ASSERT_TRUE(isFree); +} + +HWTEST_F_L0(JSNApiTests, DataView) +{ + LocalScope scope(vm_); + const int32_t length = 15; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + JSNApi::TriggerGC(vm_); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 5 : offset of byte, 7 : length + Local dataView = DataViewRef::New(vm_, arrayBuffer, 5, 7); + ASSERT_TRUE(dataView->IsDataView()); + ASSERT_EQ(dataView->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); + ASSERT_EQ(dataView->ByteLength(), 7); // 7 : size of arguments + ASSERT_EQ(dataView->ByteOffset(), 5); // 5 : size of arguments + + // 5 : offset of byte, 11 : length + dataView = DataViewRef::New(vm_, arrayBuffer, 5, 11); + ASSERT_TRUE(dataView->IsException()); +} + +HWTEST_F_L0(JSNApiTests, Int8Array) +{ + LocalScope scope(vm_); + const int32_t length = 15; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 5 : offset of byte, 6 : length + Local typedArray = Int8ArrayRef::New(vm_, arrayBuffer, 5, 6); + ASSERT_TRUE(typedArray->IsInt8Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 6); // 6 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 5); // 5 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +HWTEST_F_L0(JSNApiTests, Uint8Array) +{ + LocalScope scope(vm_); + const int32_t length = 15; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 5 : offset of byte, 6 : length + Local typedArray = Uint8ArrayRef::New(vm_, arrayBuffer, 5, 6); + ASSERT_TRUE(typedArray->IsUint8Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 6); // 6 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 5); // 5 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +HWTEST_F_L0(JSNApiTests, Uint8ClampedArray) +{ + LocalScope scope(vm_); + const int32_t length = 15; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 5 : offset of byte, 6 : length + Local typedArray = Uint8ClampedArrayRef::New(vm_, arrayBuffer, 5, 6); + ASSERT_TRUE(typedArray->IsUint8ClampedArray()); + ASSERT_EQ(typedArray->ByteLength(vm_), 6); // 6 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 5); // 5 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +HWTEST_F_L0(JSNApiTests, Int16Array) +{ + LocalScope scope(vm_); + const int32_t length = 30; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 4 : offset of byte, 6 : length + Local typedArray = Int16ArrayRef::New(vm_, arrayBuffer, 4, 6); + ASSERT_TRUE(typedArray->IsInt16Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 12); // 12 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 4); // 4 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +HWTEST_F_L0(JSNApiTests, Uint16Array) +{ + LocalScope scope(vm_); + const int32_t length = 30; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 4 : offset of byte, 6 : length + Local typedArray = Uint16ArrayRef::New(vm_, arrayBuffer, 4, 6); + ASSERT_TRUE(typedArray->IsUint16Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 12); // 12 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 4); // 4 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +HWTEST_F_L0(JSNApiTests, Uint32Array) +{ + LocalScope scope(vm_); + const int32_t length = 30; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 4 : offset of byte, 6 : length + Local typedArray = Uint32ArrayRef::New(vm_, arrayBuffer, 4, 6); + ASSERT_TRUE(typedArray->IsUint32Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 24); // 24 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 4); // 4 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +HWTEST_F_L0(JSNApiTests, Int32Array) +{ + LocalScope scope(vm_); + const int32_t length = 30; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 4 : offset of byte, 6 : length + Local typedArray = Int32ArrayRef::New(vm_, arrayBuffer, 4, 6); + ASSERT_TRUE(typedArray->IsInt32Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 24); // 24 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 4); // 4 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +HWTEST_F_L0(JSNApiTests, Float32Array) +{ + LocalScope scope(vm_); + const int32_t length = 30; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 4 : offset of byte, 6 : length + Local typedArray = Float32ArrayRef::New(vm_, arrayBuffer, 4, 6); + ASSERT_TRUE(typedArray->IsFloat32Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 24); // 24 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 4); // 4 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +HWTEST_F_L0(JSNApiTests, Float64Array) +{ + LocalScope scope(vm_); + const int32_t length = 57; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 8 : offset of byte, 6 : length + Local typedArray = Float64ArrayRef::New(vm_, arrayBuffer, 8, 6); + ASSERT_TRUE(typedArray->IsFloat64Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 48); // 48 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 8); // 8 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +HWTEST_F_L0(JSNApiTests, Error) +{ + LocalScope scope(vm_); + Local message = StringRef::NewFromUtf8(vm_, "ErrorTest"); + Local error = Exception::Error(vm_, message); + ASSERT_TRUE(error->IsError()); + + JSNApi::ThrowException(vm_, error); + ASSERT_TRUE(thread_->HasPendingException()); +} + +HWTEST_F_L0(JSNApiTests, RangeError) +{ + LocalScope scope(vm_); + Local message = StringRef::NewFromUtf8(vm_, "ErrorTest"); + Local error = Exception::RangeError(vm_, message); + ASSERT_TRUE(error->IsError()); + + JSNApi::ThrowException(vm_, error); + ASSERT_TRUE(thread_->HasPendingException()); +} + +HWTEST_F_L0(JSNApiTests, TypeError) +{ + LocalScope scope(vm_); + Local message = StringRef::NewFromUtf8(vm_, "ErrorTest"); + Local error = Exception::TypeError(vm_, message); + ASSERT_TRUE(error->IsError()); + + JSNApi::ThrowException(vm_, error); + ASSERT_TRUE(thread_->HasPendingException()); +} + +HWTEST_F_L0(JSNApiTests, ReferenceError) +{ + LocalScope scope(vm_); + Local message = StringRef::NewFromUtf8(vm_, "ErrorTest"); + Local error = Exception::ReferenceError(vm_, message); + ASSERT_TRUE(error->IsError()); + + JSNApi::ThrowException(vm_, error); + ASSERT_TRUE(thread_->HasPendingException()); +} + +HWTEST_F_L0(JSNApiTests, SyntaxError) +{ + LocalScope scope(vm_); + Local message = StringRef::NewFromUtf8(vm_, "ErrorTest"); + Local error = Exception::SyntaxError(vm_, message); + ASSERT_TRUE(error->IsError()); + + JSNApi::ThrowException(vm_, error); + ASSERT_TRUE(thread_->HasPendingException()); +} +} // namespace panda::test diff --git a/ecmascript/object_factory-inl.h b/ecmascript/object_factory-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..af7f80482494cc1f4f1c7c0bcd9a97b5ca89f824 --- /dev/null +++ b/ecmascript/object_factory-inl.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_OBJECT_FACTORY_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_OBJECT_FACTORY_INL_H + +#include "object_factory.h" +#include "ecmascript/mem/ecma_heap_manager-inl.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/lexical_env.h" + +namespace panda::ecmascript { +EcmaString *ObjectFactory::AllocNonMovableStringObject(size_t size) +{ + return reinterpret_cast(heapHelper_.AllocateNonMovableOrLargeObject(stringClass_, size)); +} + +EcmaString *ObjectFactory::AllocStringObject(size_t size) +{ + return reinterpret_cast(heapHelper_.AllocateYoungGenerationOrLargeObject(stringClass_, size)); +} + +JSHandle ObjectFactory::NewJSNativePointer(void *externalPointer, bool nonMovable) +{ + TaggedObject *header; + if (nonMovable) { + header = heapHelper_.AllocateNonMovableOrLargeObject(jsNativePointerClass_); + } else { + header = heapHelper_.AllocateYoungGenerationOrLargeObject(jsNativePointerClass_); + } + JSHandle obj(thread_, header); + obj->SetExternalPointer(externalPointer); + obj->SetDeleter(nullptr); + obj->SetData(nullptr); + return obj; +} + +JSHandle ObjectFactory::NewJSNativePointer(void *externalPointer, + DeleteEntryPoint callBack, + void *data, + bool nonMovable) +{ + TaggedObject *header; + if (nonMovable) { + header = heapHelper_.AllocateNonMovableOrLargeObject(jsNativePointerClass_); + } else { + header = heapHelper_.AllocateYoungGenerationOrLargeObject(jsNativePointerClass_); + } + JSHandle obj(thread_, header); + obj->SetExternalPointer(externalPointer); + obj->SetDeleter(callBack); + obj->SetData(data); + return obj; +} + +LexicalEnv *ObjectFactory::InlineNewLexicalEnv(int numSlots) +{ + size_t size = LexicalEnv::ComputeSize(numSlots); + auto header = heapHelper_.TryAllocateYoungGeneration(size); + if (UNLIKELY(header == nullptr)) { + return nullptr; + } + heapHelper_.SetClass(header, envClass_); + LexicalEnv *array = LexicalEnv::Cast(header); + array->InitializeWithSpecialValue(JSTaggedValue::Hole(), numSlots + LexicalEnv::RESERVED_ENV_LENGTH); + return array; +} + +template +void ObjectFactory::NewJSIntlIcuData(const JSHandle &obj, const S &icu, const DeleteEntryPoint &callback) +{ + S *icuPoint = vm_->GetRegionFactory()->New(icu); + ASSERT(icuPoint != nullptr); + JSTaggedValue data = obj->GetIcuField(); + if (data.IsHeapObject() && data.IsJSNativePointer()) { + JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject()); + void *pointer = native->GetExternalPointer(); + if (pointer != nullptr) { + native->Destroy(); + vm_->GetRegionFactory()->FreeBuffer(native->GetExternalPointer()); + } + native->SetExternalPointer(icuPoint); + return; + } + JSHandle pointer(thread_, NewJSNativePointer(icuPoint).GetTaggedValue()); + pointer->SetDeleter(callback); + pointer->SetData(vm_); + obj->SetIcuField(thread_, pointer.GetTaggedValue()); + // push uint8_t* to ecma array_data_list + vm_->PushToArrayDataList(*pointer); +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_OBJECT_FACTORY_INL_H diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9bafeca848c89f29b11b989a4dd8cac01f7f957f --- /dev/null +++ b/ecmascript/object_factory.cpp @@ -0,0 +1,2225 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecma_string_table.h" +#include "ecma_vm.h" +#include "ecmascript/accessor_data.h" +#include "ecmascript/base/error_helper.h" +#include "ecmascript/builtins.h" +#include "ecmascript/builtins/builtins_errors.h" +#include "ecmascript/builtins/builtins_global.h" +#include "ecmascript/class_linker/program_object.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_module.h" +#include "ecmascript/free_object.h" +#include "ecmascript/global_env.h" +#include "ecmascript/global_env_constants-inl.h" +#include "ecmascript/global_env_constants.h" +#include "ecmascript/ic/ic_handler.h" +#include "ecmascript/ic/profile_type_info.h" +#include "ecmascript/ic/property_box.h" +#include "ecmascript/ic/proto_change_details.h" +#include "ecmascript/jobs/micro_job_queue.h" +#include "ecmascript/jobs/pending_job.h" +#include "ecmascript/js_arguments.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_async_function.h" +#include "ecmascript/js_dataview.h" +#include "ecmascript/js_date.h" +#include "ecmascript/js_float32_array.h" +#include "ecmascript/js_float64_array.h" +#include "ecmascript/js_for_in_iterator.h" +#include "ecmascript/js_generator_object.h" +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_int16_array.h" +#include "ecmascript/js_int32_array.h" +#include "ecmascript/js_int8_array.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/js_map_iterator.h" +#include "ecmascript/js_map.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_proxy.h" +#include "ecmascript/js_realm.h" +#include "ecmascript/js_regexp.h" +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_string_iterator.h" +#include "ecmascript/js_symbol.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/js_uint16_array.h" +#include "ecmascript/js_uint32_array.h" +#include "ecmascript/js_uint8_array.h" +#include "ecmascript/js_uint8_clamped_array.h" +#include "ecmascript/js_weak_container.h" +#include "ecmascript/layout_info-inl.h" +#include "ecmascript/linked_hash_table-inl.h" +#include "ecmascript/mem/ecma_heap_manager.h" +#include "ecmascript/mem/heap-inl.h" +#include "ecmascript/mem/space.h" +#include "ecmascript/record.h" +#include "ecmascript/symbol_table-inl.h" +#include "ecmascript/template_map.h" + +namespace panda::ecmascript { +using Error = builtins::BuiltinsError; +using RangeError = builtins::BuiltinsRangeError; +using ReferenceError = builtins::BuiltinsReferenceError; +using TypeError = builtins::BuiltinsTypeError; +using URIError = builtins::BuiltinsURIError; +using SyntaxError = builtins::BuiltinsSyntaxError; +using EvalError = builtins::BuiltinsEvalError; +using ErrorType = base::ErrorType; +using ErrorHelper = base::ErrorHelper; + +ObjectFactory::ObjectFactory(JSThread *thread, Heap *heap) + : thread_(thread), heapHelper_(heap), vm_(thread->GetEcmaVM()), heap_(heap) +{ +} + +JSHandle ObjectFactory::NewEcmaDynClass(JSHClass *hclass, uint32_t size, JSType type) +{ + NewObjectHook(); + uint32_t classSize = JSHClass::SIZE; + auto *newClass = static_cast(heapHelper_.AllocateNonMovableOrLargeObject(hclass, classSize)); + newClass->Initialize(thread_, size, type, JSTaggedValue::Null()); + + return JSHandle(thread_, newClass); +} + +JSHandle ObjectFactory::NewEcmaDynClass(uint32_t size, JSType type) +{ + return NewEcmaDynClass(hclassClass_, size, type); +} + +void ObjectFactory::ObtainRootClass([[maybe_unused]] const JSHandle &globalEnv) +{ + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + hclassClass_ = JSHClass::Cast(globalConst->GetHClassClass().GetTaggedObject()); + stringClass_ = JSHClass::Cast(globalConst->GetStringClass().GetTaggedObject()); + arrayClass_ = JSHClass::Cast(globalConst->GetArrayClass().GetTaggedObject()); + dictionaryClass_ = JSHClass::Cast(globalConst->GetDictionaryClass().GetTaggedObject()); + jsNativePointerClass_ = JSHClass::Cast(globalConst->GetJSNativePointerClass().GetTaggedObject()); + freeObjectWithNoneFieldClass_ = JSHClass::Cast(globalConst->GetFreeObjectWithNoneFieldClass().GetTaggedObject()); + freeObjectWithOneFieldClass_ = JSHClass::Cast(globalConst->GetFreeObjectWithOneFieldClass().GetTaggedObject()); + freeObjectWithTwoFieldClass_ = JSHClass::Cast(globalConst->GetFreeObjectWithTwoFieldClass().GetTaggedObject()); + + completionRecordClass_ = JSHClass::Cast(globalConst->GetCompletionRecordClass().GetTaggedObject()); + generatorContextClass_ = JSHClass::Cast(globalConst->GetGeneratorContextClass().GetTaggedObject()); + programClass_ = JSHClass::Cast(globalConst->GetProgramClass().GetTaggedObject()); + ecmaModuleClass_ = JSHClass::Cast(globalConst->GetEcmaModuleClass().GetTaggedObject()); + envClass_ = JSHClass::Cast(globalConst->GetEnvClass().GetTaggedObject()); + symbolClass_ = JSHClass::Cast(globalConst->GetSymbolClass().GetTaggedObject()); + accessorDataClass_ = JSHClass::Cast(globalConst->GetAccessorDataClass().GetTaggedObject()); + internalAccessorClass_ = JSHClass::Cast(globalConst->GetInternalAccessorClass().GetTaggedObject()); + capabilityRecordClass_ = JSHClass::Cast(globalConst->GetCapabilityRecordClass().GetTaggedObject()); + reactionsRecordClass_ = JSHClass::Cast(globalConst->GetReactionsRecordClass().GetTaggedObject()); + promiseIteratorRecordClass_ = JSHClass::Cast(globalConst->GetPromiseIteratorRecordClass().GetTaggedObject()); + microJobQueueClass_ = JSHClass::Cast(globalConst->GetMicroJobQueueClass().GetTaggedObject()); + pendingJobClass_ = JSHClass::Cast(globalConst->GetPendingJobClass().GetTaggedObject()); + jsProxyOrdinaryClass_ = JSHClass::Cast(globalConst->GetJSProxyOrdinaryClass().GetTaggedObject()); + jsProxyCallableClass_ = JSHClass::Cast(globalConst->GetJSProxyCallableClass().GetTaggedObject()); + jsProxyConstructClass_ = JSHClass::Cast(globalConst->GetJSProxyConstructClass().GetTaggedObject()); + objectWrapperClass_ = JSHClass::Cast(globalConst->GetObjectWrapperClass().GetTaggedObject()); + PropertyBoxClass_ = JSHClass::Cast(globalConst->GetPropertyBoxClass().GetTaggedObject()); + protoChangeMarkerClass_ = JSHClass::Cast(globalConst->GetProtoChangeMarkerClass().GetTaggedObject()); + protoChangeDetailsClass_ = JSHClass::Cast(globalConst->GetProtoChangeDetailsClass().GetTaggedObject()); + promiseRecordClass_ = JSHClass::Cast(globalConst->GetPromiseRecordClass().GetTaggedObject()); + promiseResolvingFunctionsRecord_ = + JSHClass::Cast(globalConst->GetPromiseResolvingFunctionsRecordClass().GetTaggedObject()); + transitionHandlerClass_ = JSHClass::Cast(globalConst->GetTransitionHandlerClass().GetTaggedObject()); + prototypeHandlerClass_ = JSHClass::Cast(globalConst->GetPrototypeHandlerClass().GetTaggedObject()); + functionExtraInfo_ = JSHClass::Cast(globalConst->GetFunctionExtraInfoClass().GetTaggedObject()); + jsRealmClass_ = JSHClass::Cast(globalConst->GetJSRealmClass().GetTaggedObject()); +} + +void ObjectFactory::InitObjectFields(const TaggedObject *object) +{ + auto *klass = object->GetClass(); + auto objBodySize = klass->GetObjectSize() - TaggedObject::ObjectHeaderSize(); + ASSERT(objBodySize % JSTaggedValue::TaggedTypeSize() == 0); + int numOfFields = static_cast(objBodySize / JSTaggedValue::TaggedTypeSize()); + size_t addr = reinterpret_cast(object) + TaggedObject::ObjectHeaderSize(); + for (int i = 0; i < numOfFields; i++) { + auto *fieldAddr = reinterpret_cast(addr + i * JSTaggedValue::TaggedTypeSize()); + *fieldAddr = JSTaggedValue::Undefined().GetRawData(); + } +} + +void ObjectFactory::NewJSArrayBufferData(const JSHandle &array, int32_t length) +{ + if (length == 0) { + return; + } + + JSTaggedValue data = array->GetArrayBufferData(); + if (data != JSTaggedValue::Undefined()) { + auto *pointer = JSNativePointer::Cast(data.GetTaggedObject()); + void *arr = pointer->GetExternalPointer(); + if (arr != nullptr) { + vm_->GetRegionFactory()->FreeBuffer(arr); + } + auto newData = vm_->GetRegionFactory()->AllocateBuffer(length * sizeof(uint8_t)); + if (memset_s(newData, length, 0, length) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + pointer->SetExternalPointer(newData); + return; + } + + auto newData = vm_->GetRegionFactory()->AllocateBuffer(length * sizeof(uint8_t)); + if (memset_s(newData, length, 0, length) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + JSHandle pointer = NewJSNativePointer(newData); + array->SetArrayBufferData(thread_, pointer.GetTaggedValue()); + vm_->PushToArrayDataList(*pointer); +} + +JSHandle ObjectFactory::NewJSArrayBuffer(int32_t length) +{ + JSHandle env = vm_->GetGlobalEnv(); + + JSHandle constructor(env->GetArrayBufferFunction()); + JSHandle newTarget(constructor); + JSHandle arrayBuffer(NewJSObjectByConstructor(constructor, newTarget)); + arrayBuffer->SetArrayBufferByteLength(thread_, JSTaggedValue(length)); + if (length > 0) { + auto newData = vm_->GetRegionFactory()->AllocateBuffer(length); + if (memset_s(newData, length, 0, length) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + JSHandle pointer = NewJSNativePointer(newData); + pointer->SetDeleter(RegionFactory::FreeBufferFunc); + pointer->SetData(vm_->GetRegionFactory()); + arrayBuffer->SetArrayBufferData(thread_, pointer.GetTaggedValue()); + arrayBuffer->SetShared(thread_, JSTaggedValue::False()); + vm_->PushToArrayDataList(*pointer); + } + return arrayBuffer; +} + +JSHandle ObjectFactory::NewJSArrayBuffer(void *buffer, int32_t length, const DeleteEntryPoint &deleter, + void *data, bool share) +{ + JSHandle env = vm_->GetGlobalEnv(); + + JSHandle constructor(env->GetArrayBufferFunction()); + JSHandle newTarget(constructor); + JSHandle arrayBuffer(NewJSObjectByConstructor(constructor, newTarget)); + length = buffer == nullptr ? 0 : length; + arrayBuffer->SetArrayBufferByteLength(thread_, JSTaggedValue(length)); + if (length > 0) { + JSHandle pointer = NewJSNativePointer(buffer); + pointer->SetDeleter(deleter); + pointer->SetData(data); + arrayBuffer->SetArrayBufferData(thread_, pointer.GetTaggedValue()); + arrayBuffer->SetShared(thread_, JSTaggedValue(share)); + vm_->PushToArrayDataList(*pointer); + } + return arrayBuffer; +} + +JSHandle ObjectFactory::NewJSDataView(JSHandle buffer, int32_t offset, int32_t length) +{ + JSTaggedValue arrayLength = buffer->GetArrayBufferByteLength(); + if (!arrayLength.IsNumber()) { + THROW_TYPE_ERROR_AND_RETURN(thread_, "ArrayBuffer length error", + JSHandle(thread_, JSTaggedValue::Undefined())); + } + if (offset + length > arrayLength.GetNumber()) { + THROW_TYPE_ERROR_AND_RETURN(thread_, "offset or length error", + JSHandle(thread_, JSTaggedValue::Undefined())); + } + JSHandle env = vm_->GetGlobalEnv(); + + JSHandle constructor(env->GetDataViewFunction()); + JSHandle newTarget(constructor); + JSHandle arrayBuffer(NewJSObjectByConstructor(constructor, newTarget)); + arrayBuffer->SetDataView(thread_, JSTaggedValue::True()); + arrayBuffer->SetViewedArrayBuffer(thread_, buffer.GetTaggedValue()); + arrayBuffer->SetByteLength(thread_, JSTaggedValue(length)); + arrayBuffer->SetByteOffset(thread_, JSTaggedValue(offset)); + return arrayBuffer; +} + +void ObjectFactory::NewJSRegExpByteCodeData(const JSHandle ®exp, void *buffer, size_t size) +{ + if (buffer == nullptr) { + return; + } + + auto newBuffer = vm_->GetRegionFactory()->AllocateBuffer(size); + if (memcpy_s(newBuffer, size, buffer, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + JSTaggedValue data = regexp->GetByteCodeBuffer(); + if (data != JSTaggedValue::Undefined()) { + JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject()); + native->Destroy(); + void *bufferPointer = native->GetExternalPointer(); + if (bufferPointer != nullptr) { + vm_->GetRegionFactory()->FreeBuffer(bufferPointer); + } + native->SetExternalPointer(newBuffer); + return; + } + JSHandle pointer = NewJSNativePointer(newBuffer); + regexp->SetByteCodeBuffer(thread_, pointer.GetTaggedValue()); + regexp->SetLength(thread_, JSTaggedValue(static_cast(size))); + + // push uint8_t* to ecma array_data_list + vm_->PushToArrayDataList(*pointer); +} + +JSHandle ObjectFactory::NewEcmaDynClass(uint32_t size, JSType type, const JSHandle &prototype) +{ + NewObjectHook(); + JSHandle newClass = NewEcmaDynClass(size, type); + newClass->SetPrototype(thread_, prototype.GetTaggedValue()); + return newClass; +} + +JSHandle ObjectFactory::NewJSObject(const JSHandle &jshclass) +{ + NewObjectHook(); + JSHandle obj(thread_, JSObject::Cast(NewDynObject(jshclass, JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS))); + obj->SetElements(thread_, EmptyArray(), SKIP_BARRIER); + obj->SetProperties(thread_, EmptyArray(), SKIP_BARRIER); + return obj; +} + +JSHandle ObjectFactory::CloneProperties(const JSHandle &old) +{ + array_size_t newLength = old->GetLength(); + if (newLength == 0) { + return EmptyArray(); + } + NewObjectHook(); + auto klass = old->GetClass(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); + auto header = heapHelper_.AllocateYoungGenerationOrLargeObject(klass, size); + JSHandle newArray(thread_, header); + newArray->SetLength(newLength); + + for (array_size_t i = 0; i < newLength; i++) { + JSTaggedValue value = old->Get(i); + newArray->Set(thread_, i, value); + } + return newArray; +} + +JSHandle ObjectFactory::CloneObjectLiteral(JSHandle object) +{ + NewObjectHook(); + auto klass = object->GetClass(); + + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(klass); + JSHandle cloneObject(thread_, JSObject::Cast(header)); + + JSHandle elements(thread_, object->GetElements()); + auto newElements = CloneProperties(elements); + cloneObject->SetElements(thread_, newElements.GetTaggedValue()); + + JSHandle properties(thread_, object->GetProperties()); + auto newProperties = CloneProperties(properties); + cloneObject->SetProperties(thread_, newProperties.GetTaggedValue()); + + for (int i = 0; i < JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS; i++) { + cloneObject->SetPropertyInlinedProps(thread_, i, object->GetPropertyInlinedProps(i)); + } + return cloneObject; +} + +JSHandle ObjectFactory::CloneArrayLiteral(JSHandle object) +{ + NewObjectHook(); + auto klass = object->GetClass(); + + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(klass); + JSHandle cloneObject(thread_, JSObject::Cast(header)); + + JSHandle elements(thread_, object->GetElements()); + auto newElements = CopyArray(elements, elements->GetLength(), elements->GetLength()); + cloneObject->SetElements(thread_, newElements.GetTaggedValue()); + + JSHandle properties(thread_, object->GetProperties()); + auto newProperties = CopyArray(properties, properties->GetLength(), properties->GetLength()); + cloneObject->SetProperties(thread_, newProperties.GetTaggedValue()); + + cloneObject->SetArrayLength(thread_, object->GetArrayLength()); + for (int i = 0; i < JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS; i++) { + cloneObject->SetPropertyInlinedProps(thread_, i, object->GetPropertyInlinedProps(i)); + } + return cloneObject; +} + +JSHandle ObjectFactory::CloneProperties(const JSHandle &old, + const JSHandle &env, const JSHandle &obj, + const JSHandle &constpool) +{ + array_size_t newLength = old->GetLength(); + if (newLength == 0) { + return EmptyArray(); + } + NewObjectHook(); + auto klass = old->GetClass(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); + auto header = heapHelper_.AllocateYoungGenerationOrLargeObject(klass, size); + JSHandle newArray(thread_, header); + newArray->SetLength(newLength); + + for (array_size_t i = 0; i < newLength; i++) { + JSTaggedValue value = old->Get(i); + if (!value.IsJSFunction()) { + newArray->Set(thread_, i, value); + } else { + JSHandle valueHandle(thread_, value); + JSHandle newFunc = CloneJSFuction(valueHandle, valueHandle->GetFunctionKind()); + newFunc->SetLexicalEnv(thread_, env); + newFunc->SetHomeObject(thread_, obj); + newFunc->SetConstantPool(thread_, constpool); + newArray->Set(thread_, i, newFunc); + } + } + return newArray; +} + +JSHandle ObjectFactory::CloneObjectLiteral(JSHandle object, const JSHandle &env, + const JSHandle &constpool) +{ + NewObjectHook(); + auto klass = object->GetClass(); + + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(klass); + JSHandle cloneObject(thread_, JSObject::Cast(header)); + + JSHandle elements(thread_, object->GetElements()); + auto newElements = CloneProperties(elements, env, cloneObject, constpool); + cloneObject->SetElements(thread_, newElements.GetTaggedValue()); + + JSHandle properties(thread_, object->GetProperties()); + auto newProperties = CloneProperties(properties, env, cloneObject, constpool); + cloneObject->SetProperties(thread_, newProperties.GetTaggedValue()); + + for (int i = 0; i < JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS; i++) { + JSTaggedValue value = object->GetPropertyInlinedProps(i); + if (!value.IsJSFunction()) { + cloneObject->SetPropertyInlinedProps(thread_, i, value); + } else { + JSHandle valueHandle(thread_, value); + JSHandle newFunc = CloneJSFuction(valueHandle, valueHandle->GetFunctionKind()); + newFunc->SetLexicalEnv(thread_, env); + newFunc->SetHomeObject(thread_, cloneObject); + newFunc->SetConstantPool(thread_, constpool); + cloneObject->SetPropertyInlinedProps(thread_, i, newFunc.GetTaggedValue()); + } + } + return cloneObject; +} + +JSHandle ObjectFactory::CloneJSFuction(JSHandle obj, FunctionKind kind) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle jshclass(thread_, obj->GetJSHClass()); + JSHandle cloneFunc = NewJSFunctionByDynClass(obj->GetCallTarget(), jshclass, kind); + if (kind == FunctionKind::GENERATOR_FUNCTION) { + JSHandle objFun = env->GetObjectFunction(); + JSHandle initialGeneratorFuncPrototype = + NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSObject::SetPrototype(thread_, initialGeneratorFuncPrototype, env->GetGeneratorPrototype()); + cloneFunc->SetProtoOrDynClass(thread_, initialGeneratorFuncPrototype); + } + return cloneFunc; +} + +JSHandle ObjectFactory::NewNonMovableJSObject(const JSHandle &jshclass) +{ + NewObjectHook(); + JSHandle obj(thread_, + JSObject::Cast(NewNonMovableDynObject(jshclass, JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS))); + obj->SetElements(thread_, EmptyArray(), SKIP_BARRIER); + obj->SetProperties(thread_, EmptyArray(), SKIP_BARRIER); + return obj; +} + +JSHandle ObjectFactory::NewJSPrimitiveRef(const JSHandle &dynKlass, + const JSHandle &object) +{ + NewObjectHook(); + JSHandle obj = JSHandle::Cast(NewJSObject(dynKlass)); + obj->SetValue(thread_, object); + return obj; +} + +JSHandle ObjectFactory::NewJSArray() +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle function = env->GetArrayFunction(); + + return JSHandle(NewJSObjectByConstructor(JSHandle(function), function)); +} + +JSHandle ObjectFactory::NewJSForinIterator(const JSHandle &obj) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass(env->GetForinIteratorClass()); + + JSHandle it = JSHandle::Cast(NewJSObject(dynclass)); + it->SetObject(thread_, obj); + it->SetWasVisited(thread_, JSTaggedValue::False()); + it->SetVisitedKeys(thread_, env->GetEmptyTaggedQueue()); + it->SetRemainingKeys(thread_, env->GetEmptyTaggedQueue()); + return it; +} + +JSHandle ObjectFactory::CreateJSRegExpInstanceClass(JSHandle proto) +{ + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle regexpDynclass = NewEcmaDynClass(JSRegExp::SIZE, JSType::JS_REG_EXP, proto); + + uint32_t fieldOrder = 0; + JSHandle layoutInfoHandle = CreateLayoutInfo(1); + { + PropertyAttributes attributes = PropertyAttributes::Default(true, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder++); + layoutInfoHandle->AddKey(thread_, 0, globalConst->GetLastIndexString(), attributes); + } + + { + regexpDynclass->SetAttributes(thread_, layoutInfoHandle); + uint32_t unusedInlinedProps = regexpDynclass->GetUnusedInlinedProps(); + ASSERT(unusedInlinedProps >= fieldOrder); + regexpDynclass->SetUnusedInlinedProps(unusedInlinedProps - fieldOrder); + } + + return regexpDynclass; +} + +JSHandle ObjectFactory::CreateJSArrayInstanceClass(JSHandle proto) +{ + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle arrayDynclass = NewEcmaDynClass(JSArray::SIZE, JSType::JS_ARRAY, proto); + + uint32_t fieldOrder = 0; + ASSERT(JSArray::LENGTH_INLINE_PROPERTY_INDEX == fieldOrder); + JSHandle layoutInfoHandle = CreateLayoutInfo(1); + { + PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(true, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder++); + layoutInfoHandle->AddKey(thread_, 0, globalConst->GetLengthString(), attributes); + } + + { + arrayDynclass->SetAttributes(thread_, layoutInfoHandle); + uint32_t unusedInlinedProps = arrayDynclass->GetUnusedInlinedProps(); + ASSERT(unusedInlinedProps >= fieldOrder); + arrayDynclass->SetUnusedInlinedProps(unusedInlinedProps - fieldOrder); + } + arrayDynclass->SetIsStableJSArray(true); + arrayDynclass->SetHasConstructor(false); + + return arrayDynclass; +} + +JSHandle ObjectFactory::CreateJSArguments() +{ + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle proto = env->GetObjectFunctionPrototype(); + + JSHandle argumentsDynclass = NewEcmaDynClass(JSArguments::SIZE, JSType::JS_ARGUMENTS, proto); + + uint32_t fieldOrder = 0; + ASSERT(JSArguments::LENGTH_INLINE_PROPERTY_INDEX == fieldOrder); + JSHandle layoutInfoHandle = CreateLayoutInfo(JSArguments::LENGTH_OF_INLINE_PROPERTIES); + { + PropertyAttributes attributes = PropertyAttributes::Default(true, false, true); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder++); + layoutInfoHandle->AddKey(thread_, JSArguments::LENGTH_INLINE_PROPERTY_INDEX, globalConst->GetLengthString(), + attributes); + } + + ASSERT(JSArguments::ITERATOR_INLINE_PROPERTY_INDEX == fieldOrder); + { + PropertyAttributes attributes = PropertyAttributes::Default(true, false, true); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder++); + layoutInfoHandle->AddKey(thread_, JSArguments::ITERATOR_INLINE_PROPERTY_INDEX, + env->GetIteratorSymbol().GetTaggedValue(), attributes); + } + + { + ASSERT(JSArguments::CALLER_INLINE_PROPERTY_INDEX == fieldOrder); + PropertyAttributes attributes = PropertyAttributes::Default(false, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetIsAccessor(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder++); + layoutInfoHandle->AddKey(thread_, JSArguments::CALLER_INLINE_PROPERTY_INDEX, + thread_->GlobalConstants()->GetHandledCallerString().GetTaggedValue(), attributes); + } + + { + ASSERT(JSArguments::CALLEE_INLINE_PROPERTY_INDEX == fieldOrder); + PropertyAttributes attributes = PropertyAttributes::Default(false, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetIsAccessor(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder++); + layoutInfoHandle->AddKey(thread_, JSArguments::CALLEE_INLINE_PROPERTY_INDEX, + thread_->GlobalConstants()->GetHandledCalleeString().GetTaggedValue(), attributes); + } + + { + argumentsDynclass->SetAttributes(thread_, layoutInfoHandle); + uint32_t unusedInlinedProps = argumentsDynclass->GetUnusedInlinedProps(); + ASSERT(unusedInlinedProps >= fieldOrder); + argumentsDynclass->SetUnusedInlinedProps(unusedInlinedProps - fieldOrder); + } + return argumentsDynclass; +} + +JSHandle ObjectFactory::NewJSArguments() +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetArgumentsClass()); + JSHandle obj = JSHandle::Cast(NewJSObject(dynclass)); + return obj; +} + +JSHandle ObjectFactory::GetJSError(const ErrorType &errorType, const char *data) +{ + ASSERT_PRINT(errorType == ErrorType::ERROR || errorType == ErrorType::EVAL_ERROR || + errorType == ErrorType::RANGE_ERROR || errorType == ErrorType::REFERENCE_ERROR || + errorType == ErrorType::SYNTAX_ERROR || errorType == ErrorType::TYPE_ERROR || + errorType == ErrorType::URI_ERROR, + "The error type is not in the valid range."); + NewObjectHook(); + if (data != nullptr) { + JSHandle handleMsg = NewFromString(data); + return NewJSError(errorType, handleMsg); + } + JSHandle emptyString(thread_->GlobalConstants()->GetHandledEmptyString()); + return NewJSError(errorType, emptyString); +} + +JSHandle ObjectFactory::NewJSError(const ErrorType &errorType, const JSHandle &message) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle nativeConstructor; + switch (errorType) { + case ErrorType::RANGE_ERROR: + nativeConstructor = env->GetRangeErrorFunction(); + break; + case ErrorType::EVAL_ERROR: + nativeConstructor = env->GetEvalErrorFunction(); + break; + case ErrorType::REFERENCE_ERROR: + nativeConstructor = env->GetReferenceErrorFunction(); + break; + case ErrorType::TYPE_ERROR: + nativeConstructor = env->GetTypeErrorFunction(); + break; + case ErrorType::URI_ERROR: + nativeConstructor = env->GetURIErrorFunction(); + break; + case ErrorType::SYNTAX_ERROR: + nativeConstructor = env->GetSyntaxErrorFunction(); + break; + default: + nativeConstructor = env->GetErrorFunction(); + break; + } + JSHandle nativeFunc = JSHandle::Cast(nativeConstructor); + JSHandle nativePrototype(thread_, nativeFunc->GetFunctionPrototype()); + JSHandle ctorKey = globalConst->GetHandledConstructorString(); + + array_size_t length = 1; + JSHandle array = NewTaggedArray(length); + array->Set(thread_, 0, message.GetTaggedValue()); + JSTaggedValue obj = JSFunction::Invoke(thread_, nativePrototype, ctorKey, array); + JSHandle handleNativeInstanceObj(thread_, obj); + return handleNativeInstanceObj; +} + +JSHandle ObjectFactory::NewJSObjectByConstructor(const JSHandle &constructor, + const JSHandle &newTarget) +{ + NewObjectHook(); + JSHandle jshclass; + if (!constructor->HasFunctionPrototype() || + (constructor->GetProtoOrDynClass().IsHeapObject() && constructor->GetFunctionPrototype().IsECMAObject())) { + jshclass = JSFunction::GetInstanceJSHClass(thread_, constructor, newTarget); + } else { + JSHandle env = vm_->GetGlobalEnv(); + jshclass = JSFunction::GetInstanceJSHClass(thread_, JSHandle(env->GetObjectFunction()), newTarget); + } + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread_); + + JSHandle obj = NewJSObject(jshclass); + { + JSType type = jshclass->GetObjectType(); + switch (type) { + case JSType::JS_OBJECT: + case JSType::JS_ERROR: + case JSType::JS_EVAL_ERROR: + case JSType::JS_RANGE_ERROR: + case JSType::JS_REFERENCE_ERROR: + case JSType::JS_TYPE_ERROR: + case JSType::JS_URI_ERROR: + case JSType::JS_SYNTAX_ERROR: + case JSType::JS_ITERATOR: + break; + case JSType::JS_ARRAY: { + JSArray::Cast(*obj)->SetLength(thread_, JSTaggedValue(0)); + auto accessor = thread_->GlobalConstants()->GetArrayLengthAccessor(); + JSArray::Cast(*obj)->SetPropertyInlinedProps(thread_, JSArray::LENGTH_INLINE_PROPERTY_INDEX, accessor); + break; + } + case JSType::JS_DATE: + JSDate::Cast(*obj)->SetTimeValue(thread_, JSTaggedValue(0.0)); + JSDate::Cast(*obj)->SetLocalOffset(thread_, JSTaggedValue(JSDate::MAX_DOUBLE)); + break; + case JSType::JS_INT8_ARRAY: + JSInt8Array::Cast(*obj)->SetViewedArrayBuffer(thread_, JSTaggedValue::Undefined()); + JSInt8Array::Cast(*obj)->SetTypedArrayName(thread_, JSTaggedValue::Undefined()); + JSInt8Array::Cast(*obj)->SetByteLength(thread_, JSTaggedValue(0)); + JSInt8Array::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); + JSInt8Array::Cast(*obj)->SetArrayLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_UINT8_ARRAY: + JSUint8Array::Cast(*obj)->SetViewedArrayBuffer(thread_, JSTaggedValue::Undefined()); + JSUint8Array::Cast(*obj)->SetTypedArrayName(thread_, JSTaggedValue::Undefined()); + JSUint8Array::Cast(*obj)->SetByteLength(thread_, JSTaggedValue(0)); + JSUint8Array::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); + JSUint8Array::Cast(*obj)->SetArrayLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + JSUint8ClampedArray::Cast(*obj)->SetViewedArrayBuffer(thread_, JSTaggedValue::Undefined()); + JSUint8ClampedArray::Cast(*obj)->SetTypedArrayName(thread_, JSTaggedValue::Undefined()); + JSUint8ClampedArray::Cast(*obj)->SetByteLength(thread_, JSTaggedValue(0)); + JSUint8ClampedArray::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); + JSUint8ClampedArray::Cast(*obj)->SetArrayLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_INT16_ARRAY: + JSInt16Array::Cast(*obj)->SetViewedArrayBuffer(thread_, JSTaggedValue::Undefined()); + JSInt16Array::Cast(*obj)->SetTypedArrayName(thread_, JSTaggedValue::Undefined()); + JSInt16Array::Cast(*obj)->SetByteLength(thread_, JSTaggedValue(0)); + JSInt16Array::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); + JSInt16Array::Cast(*obj)->SetArrayLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_UINT16_ARRAY: + JSUint16Array::Cast(*obj)->SetViewedArrayBuffer(thread_, JSTaggedValue::Undefined()); + JSUint16Array::Cast(*obj)->SetTypedArrayName(thread_, JSTaggedValue::Undefined()); + JSUint16Array::Cast(*obj)->SetByteLength(thread_, JSTaggedValue(0)); + JSUint16Array::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); + JSUint16Array::Cast(*obj)->SetArrayLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_INT32_ARRAY: + JSInt32Array::Cast(*obj)->SetViewedArrayBuffer(thread_, JSTaggedValue::Undefined()); + JSInt32Array::Cast(*obj)->SetTypedArrayName(thread_, JSTaggedValue::Undefined()); + JSInt32Array::Cast(*obj)->SetByteLength(thread_, JSTaggedValue(0)); + JSInt32Array::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); + JSInt32Array::Cast(*obj)->SetArrayLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_UINT32_ARRAY: + JSUint32Array::Cast(*obj)->SetViewedArrayBuffer(thread_, JSTaggedValue::Undefined()); + JSUint32Array::Cast(*obj)->SetTypedArrayName(thread_, JSTaggedValue::Undefined()); + JSUint32Array::Cast(*obj)->SetByteLength(thread_, JSTaggedValue(0)); + JSUint32Array::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); + JSUint32Array::Cast(*obj)->SetArrayLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_FLOAT32_ARRAY: + JSFloat32Array::Cast(*obj)->SetViewedArrayBuffer(thread_, JSTaggedValue::Undefined()); + JSFloat32Array::Cast(*obj)->SetTypedArrayName(thread_, JSTaggedValue::Undefined()); + JSFloat32Array::Cast(*obj)->SetByteLength(thread_, JSTaggedValue(0)); + JSFloat32Array::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); + JSFloat32Array::Cast(*obj)->SetArrayLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_FLOAT64_ARRAY: + JSFloat64Array::Cast(*obj)->SetViewedArrayBuffer(thread_, JSTaggedValue::Undefined()); + JSFloat64Array::Cast(*obj)->SetTypedArrayName(thread_, JSTaggedValue::Undefined()); + JSFloat64Array::Cast(*obj)->SetByteLength(thread_, JSTaggedValue(0)); + JSFloat64Array::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); + JSFloat64Array::Cast(*obj)->SetArrayLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_REG_EXP: + JSRegExp::Cast(*obj)->SetByteCodeBuffer(thread_, JSTaggedValue::Undefined()); + JSRegExp::Cast(*obj)->SetOriginalSource(thread_, JSTaggedValue::Undefined()); + JSRegExp::Cast(*obj)->SetOriginalFlags(thread_, JSTaggedValue(0)); + JSRegExp::Cast(*obj)->SetLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_PRIMITIVE_REF: + JSPrimitiveRef::Cast(*obj)->SetValue(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_SET: + JSSet::Cast(*obj)->SetLinkedSet(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_MAP: + JSMap::Cast(*obj)->SetLinkedMap(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_WEAK_MAP: + JSWeakMap::Cast(*obj)->SetLinkedMap(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_WEAK_SET: + JSWeakSet::Cast(*obj)->SetLinkedSet(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_GENERATOR_OBJECT: + JSGeneratorObject::Cast(*obj)->SetGeneratorState(thread_, JSTaggedValue::Undefined()); + JSGeneratorObject::Cast(*obj)->SetGeneratorContext(thread_, JSTaggedValue::Undefined()); + JSGeneratorObject::Cast(*obj)->SetResumeResult(thread_, JSTaggedValue::Undefined()); + JSGeneratorObject::Cast(*obj)->SetResumeMode(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_STRING_ITERATOR: + JSStringIterator::Cast(*obj)->SetStringIteratorNextIndex(thread_, JSTaggedValue(0)); + JSStringIterator::Cast(*obj)->SetIteratedString(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_ARRAY_BUFFER: + JSArrayBuffer::Cast(*obj)->SetArrayBufferByteLength(thread_, JSTaggedValue(0)); + JSArrayBuffer::Cast(*obj)->SetArrayBufferData(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_PROMISE: + JSPromise::Cast(*obj)->SetPromiseState(thread_, + JSTaggedValue(static_cast(PromiseStatus::PENDING))); + JSPromise::Cast(*obj)->SetPromiseResult(thread_, JSTaggedValue::Undefined()); + JSPromise::Cast(*obj)->SetPromiseRejectReactions(thread_, GetEmptyTaggedQueue().GetTaggedValue()); + JSPromise::Cast(*obj)->SetPromiseFulfillReactions(thread_, GetEmptyTaggedQueue().GetTaggedValue()); + + JSPromise::Cast(*obj)->SetPromiseIsHandled(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_DATA_VIEW: + JSDataView::Cast(*obj)->SetDataView(thread_, JSTaggedValue(false)); + JSDataView::Cast(*obj)->SetViewedArrayBuffer(thread_, JSTaggedValue::Undefined()); + JSDataView::Cast(*obj)->SetByteLength(thread_, JSTaggedValue(0)); + JSDataView::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); + break; + case JSType::JS_FUNCTION: + case JSType::JS_GENERATOR_FUNCTION: + case JSType::JS_FORIN_ITERATOR: + case JSType::JS_MAP_ITERATOR: + case JSType::JS_SET_ITERATOR: + case JSType::JS_ARRAY_ITERATOR: + default: + UNREACHABLE(); + } + } + return obj; +} + +FreeObject *ObjectFactory::FillFreeObject(uintptr_t address, size_t size, RemoveSlots removeSlots) +{ + FreeObject *object = nullptr; + if (size >= FreeObject::SIZE_OFFSET && size < FreeObject::SIZE) { + object = reinterpret_cast(address); + object->SetClass(freeObjectWithOneFieldClass_); + } else if (size >= FreeObject::SIZE) { + object = reinterpret_cast(address); + object->SetClass(freeObjectWithTwoFieldClass_); + object->SetAvailable(size); + } else if (size == FreeObject::NEXT_OFFSET) { + object = reinterpret_cast(address); + object->SetClass(freeObjectWithNoneFieldClass_); + } else { + LOG_ECMA(INFO) << "Fill free object size is smaller"; + } + + if (removeSlots == RemoveSlots::YES) { + Region *region = Region::ObjectAddressToRange(object); + if (!region->InYoungGeneration()) { + heap_->ClearSlotsRange(region, address, address + size); + } + } + return object; +} + +TaggedObject *ObjectFactory::NewDynObject(const JSHandle &dynclass, int inobjPropCount) +{ + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(*dynclass); + if (inobjPropCount > 0) { + InitializeExtraProperties(dynclass, header, inobjPropCount); + } + return header; +} + +TaggedObject *ObjectFactory::NewNonMovableDynObject(const JSHandle &dynclass, int inobjPropCount) +{ + TaggedObject *header = heapHelper_.AllocateNonMovableOrLargeObject(*dynclass); + if (inobjPropCount > 0) { + InitializeExtraProperties(dynclass, header, inobjPropCount); + } + return header; +} + +void ObjectFactory::InitializeExtraProperties(const JSHandle &dynclass, TaggedObject *obj, int inobjPropCount) +{ + ASSERT(inobjPropCount * JSTaggedValue::TaggedTypeSize() < dynclass->GetObjectSize()); + auto paddr = reinterpret_cast(obj) + dynclass->GetObjectSize(); + JSTaggedType initVal = JSTaggedValue::Undefined().GetRawData(); + for (int i = 0; i < inobjPropCount; ++i) { + paddr -= JSTaggedValue::TaggedTypeSize(); + *reinterpret_cast(paddr) = initVal; + } +} + +JSHandle ObjectFactory::OrdinaryNewJSObjectCreate(const JSHandle &proto) +{ + NewObjectHook(); + JSHandle protoValue(proto); + JSHandle protoDyn = NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, protoValue); + JSHandle newObj = NewJSObject(protoDyn); + newObj->GetJSHClass()->SetExtensible(true); + return newObj; +} + +JSHandle ObjectFactory::NewJSFunction(const JSHandle &env, const void *nativeFunc, + FunctionKind kind) +{ + JSMethod *target = vm_->GetMethodForNativeFunction(nativeFunc); + return NewJSFunction(env, target, kind); +} + +JSHandle ObjectFactory::NewJSFunction(const JSHandle &env, JSMethod *method, FunctionKind kind) +{ + NewObjectHook(); + JSHandle dynclass; + if (kind == FunctionKind::BASE_CONSTRUCTOR) { + dynclass = JSHandle::Cast(env->GetFunctionClassWithProto()); + } else if (JSFunction::IsConstructorKind(kind)) { + dynclass = JSHandle::Cast(env->GetConstructorFunctionClass()); + } else { + dynclass = JSHandle::Cast(env->GetNormalFunctionClass()); + } + + return NewJSFunctionByDynClass(method, dynclass, kind); +} + +JSHandle ObjectFactory::CreateFunctionClass(FunctionKind kind, uint32_t size, JSType type, + const JSHandle &prototype) +{ + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle functionClass = NewEcmaDynClass(size, type, prototype); + { + functionClass->SetCallable(true); + // FunctionKind = BASE_CONSTRUCTOR + if (JSFunction::IsConstructorKind(kind)) { + functionClass->SetConstructor(true); + } + functionClass->SetExtensible(true); + } + + uint32_t fieldOrder = 0; + ASSERT(JSFunction::LENGTH_INLINE_PROPERTY_INDEX == fieldOrder); + JSHandle layoutInfoHandle = CreateLayoutInfo(JSFunction::LENGTH_OF_INLINE_PROPERTIES); + { + PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(false, false, true); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder); + layoutInfoHandle->AddKey(thread_, fieldOrder, globalConst->GetLengthString(), attributes); + fieldOrder++; + } + + ASSERT(JSFunction::NAME_INLINE_PROPERTY_INDEX == fieldOrder); + // not set name in-object property on class which may have a name() method + if (!JSFunction::IsClassConstructor(kind)) { + PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(false, false, true); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder); + layoutInfoHandle->AddKey(thread_, fieldOrder, + thread_->GlobalConstants()->GetHandledNameString().GetTaggedValue(), attributes); + fieldOrder++; + } + + if (JSFunction::HasPrototype(kind) && !JSFunction::IsClassConstructor(kind)) { + ASSERT(JSFunction::PROTOTYPE_INLINE_PROPERTY_INDEX == fieldOrder); + PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(true, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder); + layoutInfoHandle->AddKey(thread_, fieldOrder, globalConst->GetPrototypeString(), attributes); + fieldOrder++; + } else if (JSFunction::IsClassConstructor(kind)) { + ASSERT(JSFunction::CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX == fieldOrder); + PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(false, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder); + layoutInfoHandle->AddKey(thread_, fieldOrder, globalConst->GetPrototypeString(), attributes); + fieldOrder++; + } + + { + functionClass->SetAttributes(thread_, layoutInfoHandle); + uint32_t unusedInlinedProps = functionClass->GetUnusedInlinedProps(); + ASSERT(unusedInlinedProps >= fieldOrder); + functionClass->SetUnusedInlinedProps(unusedInlinedProps - fieldOrder); + } + return functionClass; +} + +JSHandle ObjectFactory::NewJSFunctionByDynClass(JSMethod *method, const JSHandle &clazz, + FunctionKind kind) +{ + NewObjectHook(); + JSHandle function = JSHandle::Cast(NewJSObject(clazz)); + clazz->SetCallable(true); + clazz->SetExtensible(true); + JSFunction::InitializeJSFunction(thread_, function, kind); + function->SetCallTarget(thread_, method); + return function; +} + +JSHandle ObjectFactory::NewJSNativeErrorFunction(const JSHandle &env, const void *nativeFunc) +{ + NewObjectHook(); + JSMethod *target = vm_->GetMethodForNativeFunction(nativeFunc); + JSHandle dynclass = JSHandle::Cast(env->GetNativeErrorFunctionClass()); + return NewJSFunctionByDynClass(target, dynclass, FunctionKind::BUILTIN_CONSTRUCTOR); +} + +JSHandle ObjectFactory::NewSpecificTypedArrayFunction(const JSHandle &env, + const void *nativeFunc) +{ + NewObjectHook(); + JSMethod *target = vm_->GetMethodForNativeFunction(nativeFunc); + JSHandle dynclass = JSHandle::Cast(env->GetSpecificTypedArrayFunctionClass()); + return NewJSFunctionByDynClass(target, dynclass, FunctionKind::BUILTIN_CONSTRUCTOR); +} + +JSHandle ObjectFactory::NewJSBoundFunction(const JSHandle &target, + const JSHandle &boundThis, + const JSHandle &args) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle proto = env->GetFunctionPrototype(); + JSHandle dynclass = NewEcmaDynClass(JSBoundFunction::SIZE, JSType::JS_BOUND_FUNCTION, proto); + + JSHandle bundleFunction = JSHandle::Cast(NewJSObject(dynclass)); + bundleFunction->SetBoundTarget(thread_, target); + bundleFunction->SetBoundThis(thread_, boundThis); + bundleFunction->SetBoundArguments(thread_, args); + dynclass->SetCallable(true); + if (target.GetTaggedValue().IsConstructor()) { + bundleFunction->SetConstructor(true); + } + JSMethod *method = + vm_->GetMethodForNativeFunction(reinterpret_cast(builtins::BuiltinsGlobal::CallJsBoundFunction)); + bundleFunction->SetCallTarget(thread_, method); + return bundleFunction; +} + +JSHandle ObjectFactory::NewJSProxyRevocFunction(const JSHandle &proxy, + const void *nativeFunc) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle dynclass = JSHandle::Cast(env->GetProxyRevocFunctionClass()); + + JSHandle revocFunction = JSHandle::Cast(NewJSObject(dynclass)); + revocFunction->SetRevocableProxy(thread_, proxy); + + JSMethod *target = vm_->GetMethodForNativeFunction(nativeFunc); + revocFunction->SetCallTarget(thread_, target); + JSHandle function = JSHandle::Cast(revocFunction); + JSFunction::InitializeJSFunction(thread_, function, FunctionKind::NORMAL_FUNCTION); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(0)); + JSHandle emptyString = globalConst->GetHandledEmptyString(); + JSHandle nameKey = globalConst->GetHandledNameString(); + PropertyDescriptor nameDesc(thread_, emptyString, false, false, true); + JSTaggedValue::DefinePropertyOrThrow(thread_, JSHandle::Cast(function), nameKey, nameDesc); + return revocFunction; +} + +JSHandle ObjectFactory::NewJSAsyncAwaitStatusFunction(const void *nativeFunc) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetAsyncAwaitStatusFunctionClass()); + + JSHandle awaitFunction = + JSHandle::Cast(NewJSObject(dynclass)); + + JSFunction::InitializeJSFunction(thread_, JSHandle::Cast(awaitFunction)); + JSMethod *target = vm_->GetMethodForNativeFunction(nativeFunc); + awaitFunction->SetCallTarget(thread_, target); + return awaitFunction; +} + +JSHandle ObjectFactory::NewJSGeneratorFunction(JSMethod *method) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + + JSHandle dynclass = JSHandle::Cast(env->GetGeneratorFunctionClass()); + JSHandle generatorFunc = JSHandle::Cast(NewJSObject(dynclass)); + JSFunction::InitializeJSFunction(thread_, generatorFunc, FunctionKind::GENERATOR_FUNCTION); + generatorFunc->SetCallTarget(thread_, method); + return generatorFunc; +} + +JSHandle ObjectFactory::NewJSGeneratorObject(JSHandle generatorFunction) +{ + NewObjectHook(); + JSHandle proto(thread_, JSHandle::Cast(generatorFunction)->GetProtoOrDynClass()); + if (!proto->IsECMAObject()) { + JSHandle realmHandle = JSObject::GetFunctionRealm(thread_, generatorFunction); + proto = realmHandle->GetGeneratorPrototype(); + } + JSHandle dynclass = NewEcmaDynClass(JSGeneratorObject::SIZE, JSType::JS_GENERATOR_OBJECT, proto); + JSHandle generatorObject = JSHandle::Cast(NewJSObject(dynclass)); + return generatorObject; +} + +JSHandle ObjectFactory::NewAsyncFunction(JSMethod *method) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetAsyncFunctionClass()); + JSHandle asyncFunction = JSHandle::Cast(NewJSObject(dynclass)); + JSFunction::InitializeJSFunction(thread_, JSHandle::Cast(asyncFunction)); + asyncFunction->SetCallTarget(thread_, method); + return asyncFunction; +} + +JSHandle ObjectFactory::NewJSAsyncFuncObject() +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle proto = env->GetInitialGenerator(); + JSHandle dynclass = NewEcmaDynClass(JSAsyncFuncObject::SIZE, JSType::JS_ASYNC_FUNC_OBJECT, proto); + JSHandle asyncFuncObject = JSHandle::Cast(NewJSObject(dynclass)); + return asyncFuncObject; +} + +JSHandle ObjectFactory::NewCompletionRecord(uint8_t type, JSHandle value) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(completionRecordClass_); + JSHandle obj(thread_, header); + obj->SetType(thread_, JSTaggedValue(static_cast(type))); + obj->SetValue(thread_, value); + return obj; +} + +JSHandle ObjectFactory::NewGeneratorContext() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(generatorContextClass_); + JSHandle obj(thread_, header); + obj->SetRegsArray(thread_, JSTaggedValue::Undefined()); + obj->SetMethod(thread_, JSTaggedValue::Undefined()); + obj->SetAcc(thread_, JSTaggedValue::Undefined()); + obj->SetNRegs(thread_, JSTaggedValue::Undefined()); + obj->SetBCOffset(thread_, JSTaggedValue::Undefined()); + obj->SetGeneratorObject(thread_, JSTaggedValue::Undefined()); + obj->SetLexicalEnv(thread_, JSTaggedValue::Undefined()); + return obj; +} + +JSHandle ObjectFactory::NewJSPrimitiveRef(const JSHandle &function, + const JSHandle &object) +{ + NewObjectHook(); + + JSHandle obj(NewJSObjectByConstructor(function, JSHandle(function))); + obj->SetValue(thread_, object); + + JSHandle env = vm_->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + if (function.GetTaggedValue() == env->GetStringFunction().GetTaggedValue()) { + JSHandle lengthStr = globalConst->GetHandledLengthString(); + + int32_t length = EcmaString::Cast(object.GetTaggedValue().GetTaggedObject())->GetLength(); + PropertyDescriptor desc(thread_, JSHandle(thread_, JSTaggedValue(length)), false, false, false); + JSTaggedValue::DefinePropertyOrThrow(thread_, JSHandle(obj), lengthStr, desc); + } + + return obj; +} + +JSHandle ObjectFactory::NewJSPrimitiveRef(PrimitiveType type, const JSHandle &object) +{ + NewObjectHook(); + ObjectFactory *factory = vm_->GetFactory(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle function; + switch (type) { + case PrimitiveType::PRIMITIVE_NUMBER: + function = env->GetNumberFunction(); + break; + case PrimitiveType::PRIMITIVE_STRING: + function = env->GetStringFunction(); + break; + case PrimitiveType::PRIMITIVE_SYMBOL: + function = env->GetSymbolFunction(); + break; + case PrimitiveType::PRIMITIVE_BOOLEAN: + function = env->GetBooleanFunction(); + break; + default: + break; + } + JSHandle funcHandle(function); + return factory->NewJSPrimitiveRef(funcHandle, object); +} + +JSHandle ObjectFactory::NewJSString(const JSHandle &str) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle stringFunc = env->GetStringFunction(); + + JSHandle obj = + JSHandle::Cast(NewJSObjectByConstructor(JSHandle(stringFunc), stringFunc)); + obj->SetValue(thread_, str, SKIP_BARRIER); + return obj; +} + +JSHandle ObjectFactory::NewGlobalEnv(JSHClass *globalEnvClass) +{ + NewObjectHook(); + // Note: Global env must be allocated in non-movable heap, since its getters will directly return + // the offsets of the properties as the address of Handles. + TaggedObject *header = heapHelper_.AllocateNonMovableOrLargeObject(globalEnvClass); + InitObjectFields(header); + return JSHandle(thread_, GlobalEnv::Cast(header)); +} + +JSHandle ObjectFactory::NewLexicalEnv(int numSlots) +{ + NewObjectHook(); + size_t size = LexicalEnv::ComputeSize(numSlots); + auto header = heapHelper_.AllocateYoungGenerationOrLargeObject(envClass_, size); + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(JSTaggedValue::Undefined(), numSlots + LexicalEnv::RESERVED_ENV_LENGTH); + return array; +} + +JSHandle ObjectFactory::NewJSSymbol() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(symbolClass_); + JSHandle obj(thread_, JSSymbol::Cast(header)); + obj->SetDescription(thread_, JSTaggedValue::Undefined()); + obj->SetFlags(thread_, JSTaggedValue(0)); + auto result = JSTaggedValue(SymbolTable::Hash(obj.GetTaggedValue())); + obj->SetHashField(thread_, result); + return obj; +} + +JSHandle ObjectFactory::NewPrivateSymbol() +{ + JSHandle obj = NewJSSymbol(); + obj->SetPrivate(thread_); + return obj; +} + +JSHandle ObjectFactory::NewPrivateNameSymbol(const JSHandle &name) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(symbolClass_); + JSHandle obj(thread_, JSSymbol::Cast(header)); + obj->SetFlags(thread_, JSTaggedValue(0)); + obj->SetPrivateNameSymbol(thread_); + obj->SetDescription(thread_, name); + auto result = JSTaggedValue(SymbolTable::Hash(name.GetTaggedValue())); + obj->SetHashField(thread_, result); + return obj; +} + +JSHandle ObjectFactory::NewWellKnownSymbol(const JSHandle &name) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(symbolClass_); + JSHandle obj(thread_, JSSymbol::Cast(header)); + obj->SetFlags(thread_, JSTaggedValue(0)); + obj->SetWellKnownSymbol(thread_); + obj->SetDescription(thread_, name); + auto result = JSTaggedValue(SymbolTable::Hash(name.GetTaggedValue())); + obj->SetHashField(thread_, result); + return obj; +} + +JSHandle ObjectFactory::NewPublicSymbol(const JSHandle &name) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(symbolClass_); + JSHandle obj(thread_, JSSymbol::Cast(header)); + obj->SetFlags(thread_, JSTaggedValue(0)); + obj->SetDescription(thread_, name); + auto result = JSTaggedValue(SymbolTable::Hash(name.GetTaggedValue())); + obj->SetHashField(thread_, result); + return obj; +} + +JSHandle ObjectFactory::NewSymbolWithTable(const JSHandle &name) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle tableHandle(env->GetRegisterSymbols()); + if (tableHandle->ContainsKey(thread_, name.GetTaggedValue())) { + JSTaggedValue objValue = tableHandle->GetSymbol(name.GetTaggedValue()); + return JSHandle(thread_, objValue); + } + + JSHandle obj = NewPublicSymbol(name); + JSHandle valueHandle(obj); + JSHandle keyHandle(name); + SymbolTable *table = SymbolTable::Insert(thread_, tableHandle, keyHandle, valueHandle); + env->SetRegisterSymbols(thread_, JSTaggedValue(table)); + return obj; +} + +JSHandle ObjectFactory::NewPrivateNameSymbolWithChar(const char *description) +{ + NewObjectHook(); + JSHandle string = NewFromString(description); + return NewPrivateNameSymbol(JSHandle(string)); +} + +JSHandle ObjectFactory::NewWellKnownSymbolWithChar(const char *description) +{ + NewObjectHook(); + JSHandle string = NewFromString(description); + return NewWellKnownSymbol(JSHandle(string)); +} + +JSHandle ObjectFactory::NewPublicSymbolWithChar(const char *description) +{ + NewObjectHook(); + JSHandle string = NewFromString(description); + return NewPublicSymbol(JSHandle(string)); +} + +JSHandle ObjectFactory::NewSymbolWithTableWithChar(const char *description) +{ + NewObjectHook(); + JSHandle string = NewFromString(description); + return NewSymbolWithTable(JSHandle(string)); +} + +JSHandle ObjectFactory::NewAccessorData() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(accessorDataClass_); + JSHandle acc(thread_, AccessorData::Cast(header)); + acc->SetGetter(thread_, JSTaggedValue::Undefined()); + acc->SetSetter(thread_, JSTaggedValue::Undefined()); + return acc; +} + +JSHandle ObjectFactory::NewInternalAccessor(void *setter, void *getter) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateNonMovableOrLargeObject(internalAccessorClass_); + JSHandle obj(thread_, AccessorData::Cast(header)); + if (setter != nullptr) { + JSHandle setFunc = NewJSNativePointer(setter, true); + obj->SetSetter(thread_, setFunc.GetTaggedValue()); + } else { + JSTaggedValue setFunc = JSTaggedValue::Undefined(); + obj->SetSetter(thread_, setFunc); + ASSERT(!obj->HasSetter()); + } + JSHandle getFunc = NewJSNativePointer(getter, true); + obj->SetGetter(thread_, getFunc); + return obj; +} + +JSHandle ObjectFactory::NewPromiseCapability() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(capabilityRecordClass_); + JSHandle obj(thread_, header); + obj->SetPromise(thread_, JSTaggedValue::Undefined()); + obj->SetResolve(thread_, JSTaggedValue::Undefined()); + obj->SetReject(thread_, JSTaggedValue::Undefined()); + return obj; +} + +JSHandle ObjectFactory::NewPromiseReaction() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(reactionsRecordClass_); + JSHandle obj(thread_, header); + obj->SetPromiseCapability(thread_, JSTaggedValue::Undefined()); + obj->SetHandler(thread_, JSTaggedValue::Undefined()); + obj->SetType(thread_, JSTaggedValue::Undefined()); + return obj; +} + +JSHandle ObjectFactory::NewPromiseIteratorRecord(const JSHandle &itor, + const JSHandle &done) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(promiseIteratorRecordClass_); + JSHandle obj(thread_, header); + obj->SetIterator(thread_, itor.GetTaggedValue()); + obj->SetDone(thread_, done.GetTaggedValue()); + return obj; +} + +JSHandle ObjectFactory::NewMicroJobQueue() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateNonMovableOrLargeObject(microJobQueueClass_); + JSHandle obj(thread_, header); + obj->SetPromiseJobQueue(thread_, GetEmptyTaggedQueue().GetTaggedValue()); + obj->SetScriptJobQueue(thread_, GetEmptyTaggedQueue().GetTaggedValue()); + return obj; +} + +JSHandle ObjectFactory::NewPendingJob(const JSHandle &func, + const JSHandle &argv) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(pendingJobClass_); + JSHandle obj(thread_, header); + obj->SetJob(thread_, func.GetTaggedValue()); + obj->SetArguments(thread_, argv.GetTaggedValue()); + return obj; +} + +JSHandle ObjectFactory::NewFunctionExtraInfo(const JSHandle &callBack, + const JSHandle &data) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(functionExtraInfo_); + JSHandle obj(thread_, header); + obj->SetCallback(thread_, callBack.GetTaggedValue()); + obj->SetData(thread_, data.GetTaggedValue()); + return obj; +} + +JSHandle ObjectFactory::NewJSProxy(const JSHandle &target, + const JSHandle &handler) +{ + NewObjectHook(); + TaggedObject *header = nullptr; + if (target->IsCallable()) { + header = target->IsConstructor() ? heapHelper_.AllocateYoungGenerationOrLargeObject(jsProxyConstructClass_) + : heapHelper_.AllocateYoungGenerationOrLargeObject(jsProxyCallableClass_); + } else { + header = heapHelper_.AllocateYoungGenerationOrLargeObject(jsProxyOrdinaryClass_); + } + + JSHandle proxy(thread_, header); + JSMethod *method = nullptr; + if (target->IsCallable()) { + JSMethod *nativeMethod = + vm_->GetMethodForNativeFunction(reinterpret_cast(builtins::BuiltinsGlobal::CallJsProxy)); + proxy->SetCallTarget(thread_, nativeMethod); + } + proxy->SetMethod(method); + + proxy->SetTarget(thread_, target.GetTaggedValue()); + proxy->SetHandler(thread_, handler.GetTaggedValue()); + return proxy; +} + +JSHandle ObjectFactory::NewJSRealm() +{ + NewObjectHook(); + + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynClassClassHandle = NewEcmaDynClass(nullptr, JSHClass::SIZE, JSType::HCLASS); + JSHClass *dynclass = reinterpret_cast(dynClassClassHandle.GetTaggedValue().GetTaggedObject()); + dynclass->SetClass(dynclass); + JSHandle realmEnvClass = NewEcmaDynClass(*dynClassClassHandle, GlobalEnv::SIZE, JSType::GLOBAL_ENV); + JSHandle realmEnvHandle = NewGlobalEnv(*realmEnvClass); + + ObtainRootClass(env); + realmEnvHandle->SetEmptyArray(thread_, NewEmptyArray()); + realmEnvHandle->SetEmptyTaggedQueue(thread_, NewTaggedQueue(0)); + realmEnvHandle->SetTemplateMap(thread_, JSTaggedValue(TemplateMap::Create(thread_))); + + Builtins builtins; + builtins.Initialize(realmEnvHandle, thread_); + JSHandle protoValue = thread_->GlobalConstants()->GetHandledJSRealmClass(); + JSHandle dynHandle = NewEcmaDynClass(JSRealm::SIZE, JSType::JS_REALM, protoValue); + JSHandle realm(NewJSObject(dynHandle)); + realm->SetGlobalEnv(thread_, realmEnvHandle.GetTaggedValue()); + + JSHandle realmObj = realmEnvHandle->GetJSGlobalObject(); + JSHandle realmkey(thread_->GlobalConstants()->GetHandledGlobalString()); + PropertyDescriptor realmDesc(thread_, JSHandle::Cast(realmObj), true, false, true); + [[maybe_unused]] bool status = + JSObject::DefineOwnProperty(thread_, JSHandle::Cast(realm), realmkey, realmDesc); + ASSERT_PRINT(status == true, "Realm defineOwnProperty failed"); + + return realm; +} + +JSHandle ObjectFactory::NewEmptyArray() +{ + NewObjectHook(); + + auto header = heapHelper_.AllocateNonMovableOrLargeObject(arrayClass_, sizeof(TaggedArray)); + JSHandle array(thread_, header); + array->SetLength(0); + return array; +} + +JSHandle ObjectFactory::NewTaggedArray(array_size_t length, JSTaggedValue initVal, bool nonMovable) +{ + if (nonMovable) { + return NewTaggedArray(length, initVal, MemSpaceType::NON_MOVABLE); + } + return NewTaggedArray(length, initVal, MemSpaceType::SEMI_SPACE); +} + +JSHandle ObjectFactory::NewTaggedArray(array_size_t length, JSTaggedValue initVal, MemSpaceType spaceType) +{ + NewObjectHook(); + if (length == 0) { + return EmptyArray(); + } + + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + TaggedObject *header = nullptr; + switch (spaceType) { + case MemSpaceType::SEMI_SPACE: + header = heapHelper_.AllocateYoungGenerationOrLargeObject(arrayClass_, size); + break; + case MemSpaceType::OLD_SPACE: + header = heapHelper_.AllocateOldGenerationOrLargeObject(arrayClass_, size); + break; + case MemSpaceType::NON_MOVABLE: + header = heapHelper_.AllocateNonMovableOrLargeObject(arrayClass_, size); + break; + default: + UNREACHABLE(); + } + + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(initVal, length); + return array; +} + +JSHandle ObjectFactory::NewTaggedArray(array_size_t length, JSTaggedValue initVal) +{ + NewObjectHook(); + if (length == 0) { + return EmptyArray(); + } + + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + auto header = heapHelper_.AllocateYoungGenerationOrLargeObject(arrayClass_, size); + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(initVal, length); + return array; +} + +JSHandle ObjectFactory::NewDictionaryArray(array_size_t length) +{ + NewObjectHook(); + + ASSERT(length > 0); + + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + auto header = heapHelper_.AllocateYoungGenerationOrLargeObject(dictionaryClass_, size); + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(JSTaggedValue::Undefined(), length); + + return array; +} + +JSHandle ObjectFactory::ExtendArray(const JSHandle &old, array_size_t length, + JSTaggedValue initVal) +{ + ASSERT(length > old->GetLength()); + + NewObjectHook(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + auto header = heapHelper_.AllocateYoungGenerationOrLargeObject(arrayClass_, size); + JSHandle newArray(thread_, header); + newArray->SetLength(length); + + array_size_t oldLength = old->GetLength(); + for (array_size_t i = 0; i < oldLength; i++) { + JSTaggedValue value = old->Get(i); + newArray->Set(thread_, i, value); + } + + for (array_size_t i = oldLength; i < length; i++) { + newArray->Set(thread_, i, initVal); + } + + return newArray; +} + +JSHandle ObjectFactory::CopyPartArray(const JSHandle &old, array_size_t start, + array_size_t end) +{ + ASSERT(start <= end); + ASSERT(end <= old->GetLength()); + + array_size_t newLength = end - start; + if (newLength == 0) { + return EmptyArray(); + } + + NewObjectHook(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); + auto header = heapHelper_.AllocateYoungGenerationOrLargeObject(arrayClass_, size); + JSHandle newArray(thread_, header); + newArray->SetLength(newLength); + + for (array_size_t i = 0; i < newLength; i++) { + JSTaggedValue value = old->Get(i + start); + if (value.IsHole()) { + break; + } + newArray->Set(thread_, i, value); + } + return newArray; +} + +JSHandle ObjectFactory::CopyArray(const JSHandle &old, + [[maybe_unused]] array_size_t oldLength, array_size_t newLength, + JSTaggedValue initVal) +{ + if (newLength == 0) { + return EmptyArray(); + } + if (newLength > oldLength) { + return ExtendArray(old, newLength, initVal); + } + + NewObjectHook(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); + auto header = heapHelper_.AllocateYoungGenerationOrLargeObject(arrayClass_, size); + JSHandle newArray(thread_, header); + newArray->SetLength(newLength); + + for (array_size_t i = 0; i < newLength; i++) { + JSTaggedValue value = old->Get(i); + newArray->Set(thread_, i, value); + } + + return newArray; +} + +JSHandle ObjectFactory::CreateLayoutInfo(int properties, JSTaggedValue initVal) +{ + int arrayLength = LayoutInfo::ComputeArrayLength(LayoutInfo::ComputeGrowCapacity(properties)); + JSHandle layoutInfoHandle = JSHandle::Cast(NewTaggedArray(arrayLength, initVal)); + layoutInfoHandle->SetNumberOfElements(thread_, 0); + return layoutInfoHandle; +} + +JSHandle ObjectFactory::ExtendLayoutInfo(const JSHandle &old, int properties, + JSTaggedValue initVal) +{ + ASSERT(properties > old->NumberOfElements()); + NewObjectHook(); + int arrayLength = LayoutInfo::ComputeArrayLength(LayoutInfo::ComputeGrowCapacity(properties)); + return JSHandle(ExtendArray(JSHandle(old), arrayLength, initVal)); +} + +JSHandle ObjectFactory::CopyLayoutInfo(const JSHandle &old) +{ + NewObjectHook(); + int newLength = old->GetLength(); + return JSHandle(CopyArray(JSHandle::Cast(old), newLength, newLength)); +} + +JSHandle ObjectFactory::CopyAndReSort(const JSHandle &old, int end, int capacity) +{ + NewObjectHook(); + ASSERT(capacity >= end); + JSHandle newArr = CreateLayoutInfo(capacity); + Span sp(old->GetProperties(), end); + int i = 0; + for (; i < end; i++) { + newArr->AddKey(thread_, i, sp[i].key_, PropertyAttributes(sp[i].attr_)); + } + + return newArr; +} + +JSHandle ObjectFactory::NewConstantPool(uint32_t capacity) +{ + NewObjectHook(); + if (capacity == 0) { + return JSHandle::Cast(EmptyArray()); + } + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), capacity); + auto header = heapHelper_.AllocateNonMovableOrLargeObject(arrayClass_, size); + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(JSTaggedValue::Undefined(), capacity); + return array; +} + +JSHandle ObjectFactory::NewProgram() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(programClass_); + JSHandle p(thread_, header); + p->SetLocation(thread_, JSTaggedValue::Undefined()); + p->SetConstantPool(thread_, JSTaggedValue::Undefined()); + p->SetMainFunction(thread_, JSTaggedValue::Undefined()); + p->SetMethodsData(nullptr); + return p; +} + +JSHandle ObjectFactory::NewEmptyEcmaModule() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(ecmaModuleClass_); + JSHandle module(thread_, header); + module->SetNameDictionary(thread_, JSTaggedValue::Undefined()); + return module; +} + +JSHandle ObjectFactory::NewFromUtf8(const uint8_t *utf8Data, uint32_t utf8Len) +{ + NewObjectHook(); + return GetStringFromStringTable(utf8Data, utf8Len); +} + +JSHandle ObjectFactory::NewFromString(const CString &data) +{ + NewObjectHook(); + auto utf8Data = reinterpret_cast(data.c_str()); + auto utf8Len = data.length(); + return GetStringFromStringTable(utf8Data, utf8Len); +} + +JSHandle ObjectFactory::NewFromStdString(const std::string &data) +{ + return NewFromUtf8(reinterpret_cast(data.c_str()), data.size()); +} + +JSHandle ObjectFactory::NewFromUtf16(const uint16_t *utf16Data, uint32_t utf16Len) +{ + NewObjectHook(); + return GetStringFromStringTable(utf16Data, utf16Len); +} + +JSHandle ObjectFactory::NewFromString(EcmaString *str) +{ + NewObjectHook(); + return GetStringFromStringTable(str); +} + +JSHandle ObjectFactory::ConcatFromString(const JSHandle &prefix, + const JSHandle &suffix) +{ + NewObjectHook(); + EcmaString *concatString = EcmaString::Concat(prefix, suffix, vm_); + return GetStringFromStringTable(concatString); +} + +JSHandle ObjectFactory::ConcatFromChar(const char *prefix, const char *suffix) +{ + NewObjectHook(); + CString const &concatString = CString(prefix) + CString(suffix); + char const *concatData = concatString.c_str(); + auto utf8Data = reinterpret_cast(concatData); + JSHandle oldString = GetStringFromStringTable(utf8Data, base::utf_helper::Utf8ToUtf16Size(utf8Data)); + return oldString; +} + +JSHandle ObjectFactory::SubString(const JSHandle &src, uint32_t start, uint32_t utf16_length) +{ + NewObjectHook(); + EcmaString *sub_string = EcmaString::FastSubString(src, start, utf16_length, vm_); + return GetStringFromStringTable(sub_string); +} + +JSHandle ObjectFactory::NewFromUtf8Literal(const uint8_t *utf8Data, uint32_t utf8Len) +{ + NewObjectHook(); + return JSHandle(thread_, EcmaString::CreateFromUtf8(utf8Data, utf8Len, vm_)); +} + +JSHandle ObjectFactory::NewFromUtf16Literal(const uint16_t *utf16Data, uint32_t utf16Len) +{ + NewObjectHook(); + return JSHandle(thread_, EcmaString::CreateFromUtf16(utf16Data, utf16Len, vm_)); +} + +JSHandle ObjectFactory::GetEmptyString() const +{ + return JSHandle(thread_->GlobalConstants()->GetHandledEmptyString()); +} + +JSHandle ObjectFactory::EmptyArray() const +{ + JSHandle env = vm_->GetGlobalEnv(); + return JSHandle(env->GetEmptyArray()); +} + +JSHandle ObjectFactory::NewObjectWrapper(const JSHandle &value) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(objectWrapperClass_); + ObjectWrapper *obj = ObjectWrapper::Cast(header); + obj->SetValue(thread_, value); + return JSHandle(thread_, obj); +} + +JSHandle ObjectFactory::GetStringFromStringTable(const uint8_t *utf8Data, uint32_t utf8Len) const +{ + if (utf8Len == 0) { + return GetEmptyString(); + } + auto stringTable = vm_->GetEcmaStringTable(); + return JSHandle(thread_, stringTable->GetOrInternString(utf8Data, utf8Len)); +} + +JSHandle ObjectFactory::GetStringFromStringTable(const uint16_t *utf16Data, uint32_t utf16Len) const +{ + if (utf16Len == 0) { + return GetEmptyString(); + } + auto stringTable = vm_->GetEcmaStringTable(); + return JSHandle(thread_, stringTable->GetOrInternString(utf16Data, utf16Len)); +} + +JSHandle ObjectFactory::GetStringFromStringTable(EcmaString *string) const +{ + ASSERT(string != nullptr); + if (string->GetLength() == 0) { + return GetEmptyString(); + } + auto stringTable = vm_->GetEcmaStringTable(); + return JSHandle(thread_, stringTable->GetOrInternString(string)); +} + +// NB! don't do special case for C0 80, it means '\u0000', so don't convert to UTF-8 +EcmaString *ObjectFactory::GetRawStringFromStringTable(const uint8_t *mutf8Data, uint32_t utf16Len) const +{ + if (UNLIKELY(utf16Len == 0)) { + return *GetEmptyString(); + } + + if (utf::IsMUtf8OnlySingleBytes(mutf8Data)) { + return EcmaString::Cast(vm_->GetEcmaStringTable()->GetOrInternString(mutf8Data, utf16Len)); + } + + CVector utf16Data(utf16Len); + auto len = utf::ConvertRegionMUtf8ToUtf16(mutf8Data, utf16Data.data(), utf::Mutf8Size(mutf8Data), utf16Len, 0); + return EcmaString::Cast(vm_->GetEcmaStringTable()->GetOrInternString(utf16Data.data(), len)); +} + +JSHandle ObjectFactory::NewPropertyBox(const JSHandle &value) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(PropertyBoxClass_); + JSHandle box(thread_, header); + box->SetValue(thread_, value); + return box; +} + +JSHandle ObjectFactory::NewProtoChangeMarker() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(protoChangeMarkerClass_); + JSHandle marker(thread_, header); + marker->SetHasChanged(false); + return marker; +} + +JSHandle ObjectFactory::NewProtoChangeDetails() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(protoChangeDetailsClass_); + JSHandle protoInfo(thread_, header); + protoInfo->SetChangeListener(thread_, JSTaggedValue(0)); + protoInfo->SetRegisterIndex(thread_, JSTaggedValue(ProtoChangeDetails::UNREGISTERED)); + return protoInfo; +} + +JSHandle ObjectFactory::NewProfileTypeInfo(uint32_t length) +{ + NewObjectHook(); + ASSERT(length > 0); + + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + auto header = heapHelper_.AllocateYoungGenerationOrLargeObject(arrayClass_, size); + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(JSTaggedValue::Undefined(), length); + + return array; +} + +// static +void ObjectFactory::NewObjectHook() +{ +#if !defined(NDEBUG) || defined(FORCE_GC_TEST) + if (!isTriggerGc_) { + return; + } + if (vm_->IsInitialized()) { + if (triggerSemiGC_) { + vm_->CollectGarbage(TriggerGCType::SEMI_GC); + } else { + vm_->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + } + } +#endif +} + +void ObjectFactory::SetTriggerGc(bool flag, bool triggerSemiGC) +{ + isTriggerGc_ = flag; + triggerSemiGC_ = triggerSemiGC; +} + +JSHandle ObjectFactory::NewTaggedQueue(array_size_t length) +{ + NewObjectHook(); + + array_size_t queueLength = TaggedQueue::QueueToArrayIndex(length); + auto queue = JSHandle::Cast(NewTaggedArray(queueLength, JSTaggedValue::Hole())); + queue->SetStart(thread_, JSTaggedValue(0)); // equal to 0 when add 1. + queue->SetEnd(thread_, JSTaggedValue(0)); + queue->SetCapacity(thread_, JSTaggedValue(length)); + + return queue; +} + +JSHandle ObjectFactory::GetEmptyTaggedQueue() const +{ + JSHandle env = vm_->GetGlobalEnv(); + return JSHandle(env->GetEmptyTaggedQueue()); +} + +JSHandle ObjectFactory::NewJSSetIterator(const JSHandle &set, IterationKind kind) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle protoValue = env->GetSetIteratorPrototype(); + JSHandle dynHandle = NewEcmaDynClass(JSSetIterator::SIZE, JSType::JS_SET_ITERATOR, protoValue); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedSet(thread_, set->GetLinkedSet()); + iter->SetNextIndex(thread_, JSTaggedValue(0)); + iter->SetIterationKind(thread_, JSTaggedValue(static_cast(kind))); + return iter; +} + +JSHandle ObjectFactory::NewJSMapIterator(const JSHandle &map, IterationKind kind) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle protoValue = env->GetMapIteratorPrototype(); + JSHandle dynHandle = NewEcmaDynClass(JSMapIterator::SIZE, JSType::JS_MAP_ITERATOR, protoValue); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedMap(thread_, map->GetLinkedMap()); + iter->SetNextIndex(thread_, JSTaggedValue(0)); + iter->SetIterationKind(thread_, JSTaggedValue(static_cast(kind))); + return iter; +} + +JSHandle ObjectFactory::NewJSArrayIterator(const JSHandle &array, IterationKind kind) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle protoValue = env->GetArrayIteratorPrototype(); + JSHandle dynHandle = NewEcmaDynClass(JSArrayIterator::SIZE, JSType::JS_ARRAY_ITERATOR, protoValue); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedArray(thread_, array); + iter->SetNextIndex(thread_, JSTaggedValue(0)); + iter->SetIterationKind(thread_, JSTaggedValue(static_cast(kind))); + return iter; +} + +JSHandle ObjectFactory::CreateJSPromiseReactionsFunction(const void *nativeFunc) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetPromiseReactionFunctionClass()); + + JSHandle reactionsFunction = + JSHandle::Cast(NewJSObject(dynclass)); + reactionsFunction->SetPromise(thread_, JSTaggedValue::Hole()); + reactionsFunction->SetAlreadyResolved(thread_, JSTaggedValue::Hole()); + JSMethod *method = vm_->GetMethodForNativeFunction(nativeFunc); + reactionsFunction->SetCallTarget(thread_, method); + JSHandle function = JSHandle::Cast(reactionsFunction); + JSFunction::InitializeJSFunction(thread_, function); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(1)); + return reactionsFunction; +} + +JSHandle ObjectFactory::CreateJSPromiseExecutorFunction(const void *nativeFunc) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetPromiseExecutorFunctionClass()); + JSHandle executorFunction = + JSHandle::Cast(NewJSObject(dynclass)); + executorFunction->SetCapability(thread_, JSTaggedValue::Hole()); + JSMethod *method = vm_->GetMethodForNativeFunction(nativeFunc); + executorFunction->SetCallTarget(thread_, method); + executorFunction->SetCapability(thread_, JSTaggedValue::Undefined()); + JSHandle function = JSHandle::Cast(executorFunction); + JSFunction::InitializeJSFunction(thread_, function, FunctionKind::NORMAL_FUNCTION); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(FunctionLength::TWO)); + return executorFunction; +} + +JSHandle ObjectFactory::NewJSPromiseAllResolveElementFunction( + const void *nativeFunc) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetPromiseAllResolveElementFunctionClass()); + JSHandle function = + JSHandle::Cast(NewJSObject(dynclass)); + JSFunction::InitializeJSFunction(thread_, JSHandle::Cast(function)); + JSMethod *method = vm_->GetMethodForNativeFunction(nativeFunc); + function->SetCallTarget(thread_, method); + JSFunction::SetFunctionLength(thread_, JSHandle::Cast(function), JSTaggedValue(1)); + return function; +} + +EcmaString *ObjectFactory::InternString(const JSHandle &key) +{ + EcmaString *str = EcmaString::Cast(key->GetTaggedObject()); + if (str->IsInternString()) { + return str; + } + + EcmaStringTable *stringTable = vm_->GetEcmaStringTable(); + return stringTable->GetOrInternString(str); +} + +TransitionHandler *ObjectFactory::NewTransitionHandler() +{ + NewObjectHook(); + TransitionHandler *handler = + TransitionHandler::Cast(heapHelper_.AllocateYoungGenerationOrLargeObject(transitionHandlerClass_)); + return handler; +} + +PrototypeHandler *ObjectFactory::NewPrototypeHandler() +{ + NewObjectHook(); + PrototypeHandler *handler = + PrototypeHandler::Cast(heapHelper_.AllocateYoungGenerationOrLargeObject(prototypeHandlerClass_)); + handler->SetHandlerInfo(thread_, JSTaggedValue::Undefined()); + handler->SetProtoCell(thread_, JSTaggedValue::Undefined()); + handler->SetHolder(thread_, JSTaggedValue::Undefined()); + return handler; +} + +JSHandle ObjectFactory::NewPromiseRecord() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(promiseRecordClass_); + JSHandle obj(thread_, header); + obj->SetValue(thread_, JSTaggedValue::Undefined()); + return obj; +} + +JSHandle ObjectFactory::NewResolvingFunctionsRecord() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrLargeObject(promiseResolvingFunctionsRecord_); + JSHandle obj(thread_, header); + obj->SetResolveFunction(thread_, JSTaggedValue::Undefined()); + obj->SetRejectFunction(thread_, JSTaggedValue::Undefined()); + return obj; +} + +JSHandle ObjectFactory::CreateObjectClass(const JSHandle &keys, + const JSHandle &values) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle proto = env->GetObjectFunctionPrototype(); + + JSHandle objClass = NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, proto); + + uint32_t fieldOrder = 0; + JSMutableHandle key(thread_, JSTaggedValue::Undefined()); + JSMutableHandle value(thread_, JSTaggedValue::Undefined()); + uint32_t length = keys->GetLength(); + JSHandle layoutInfoHandle = CreateLayoutInfo(length); + while (fieldOrder < length) { + key.Update(keys->Get(fieldOrder)); + value.Update(values->Get(fieldOrder)); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + PropertyAttributes attributes = PropertyAttributes::Default(true, true, true); + + if (values->Get(fieldOrder).IsAccessorData()) { + attributes.SetIsAccessor(true); + } + + if (fieldOrder >= JSObject::MIN_PROPERTIES_LENGTH) { + attributes.SetIsInlinedProps(false); + } else { + attributes.SetIsInlinedProps(true); + } + + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder); + layoutInfoHandle->AddKey(thread_, fieldOrder, key.GetTaggedValue(), attributes); + fieldOrder++; + } + + { + objClass->SetExtensible(true); + objClass->SetIsLiteral(true); + objClass->SetAttributes(thread_, layoutInfoHandle); + + if (fieldOrder > JSObject::MIN_PROPERTIES_LENGTH) { + uint32_t unusedNonInlinedProps = objClass->GetUnusedNonInlinedProps(); + ASSERT(unusedNonInlinedProps >= (fieldOrder - JSObject::MIN_PROPERTIES_LENGTH)); + objClass->SetUnusedNonInlinedProps(unusedNonInlinedProps - (fieldOrder - JSObject::MIN_PROPERTIES_LENGTH)); + objClass->SetUnusedInlinedProps(0); + } else { + uint32_t unusedInlinedProps = objClass->GetUnusedInlinedProps(); + ASSERT(unusedInlinedProps >= fieldOrder); + objClass->SetUnusedInlinedProps(unusedInlinedProps - fieldOrder); + } + } + return objClass; +} + +JSHandle ObjectFactory::CreateObjectClass(const JSHandle &properties, size_t length) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle proto = env->GetObjectFunctionPrototype(); + JSHandle objClass = NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, proto); + + uint32_t fieldOrder = 0; + JSMutableHandle key(thread_, JSTaggedValue::Undefined()); + JSHandle layoutInfoHandle = CreateLayoutInfo(length); + while (fieldOrder < length) { + key.Update(properties->Get(fieldOrder * 2)); // 2: Meaning to double + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + PropertyAttributes attributes = PropertyAttributes::Default(true, true, true); + + if (properties->Get(fieldOrder * 2 + 1).IsAccessor()) { // 2: Meaning to double + attributes.SetIsAccessor(true); + } + + if (fieldOrder >= JSObject::MIN_PROPERTIES_LENGTH) { + attributes.SetIsInlinedProps(false); + } else { + attributes.SetIsInlinedProps(true); + } + + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder); + layoutInfoHandle->AddKey(thread_, fieldOrder, key.GetTaggedValue(), attributes); + fieldOrder++; + } + + { + objClass->SetExtensible(true); + objClass->SetIsLiteral(true); + objClass->SetAttributes(thread_, layoutInfoHandle); + if (fieldOrder > JSObject::MIN_PROPERTIES_LENGTH) { + uint32_t unusedNonInlinedProps = objClass->GetUnusedNonInlinedProps(); + ASSERT(unusedNonInlinedProps >= (fieldOrder - JSObject::MIN_PROPERTIES_LENGTH)); + objClass->SetUnusedNonInlinedProps(unusedNonInlinedProps - (fieldOrder - JSObject::MIN_PROPERTIES_LENGTH)); + objClass->SetUnusedInlinedProps(0); + } else { + uint32_t unusedInlinedProps = objClass->GetUnusedInlinedProps(); + ASSERT(unusedInlinedProps >= fieldOrder); + objClass->SetUnusedInlinedProps(unusedInlinedProps - fieldOrder); + } + } + return objClass; +} + +JSHandle ObjectFactory::NewJSObjectByClass(const JSHandle &keys, + const JSHandle &values) +{ + NewObjectHook(); + JSHandle dynclass = CreateObjectClass(keys, values); + JSHandle obj = NewJSObject(dynclass); + return obj; +} + +JSHandle ObjectFactory::NewJSObjectByClass(const JSHandle &properties, size_t length) +{ + NewObjectHook(); + JSHandle dynclass = CreateObjectClass(properties, length); + JSHandle obj = NewJSObject(dynclass); + return obj; +} + +JSHandle ObjectFactory::NewEmptyJSObject() +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle builtinObj = env->GetObjectFunction(); + return NewJSObjectByConstructor(JSHandle(builtinObj), builtinObj); +} + +EcmaString *ObjectFactory::ResolveString(uint32_t stringId) +{ + JSMethod *caller = EcmaFrameHandler(thread_).GetMethod(); + auto *pf = caller->GetPandaFile(); + auto id = panda_file::File::EntityId(stringId); + auto foundStr = pf->GetStringData(id); + + return GetRawStringFromStringTable(foundStr.data, foundStr.utf16_length); +} + +uintptr_t ObjectFactory::NewSpaceBySnapShotAllocator(size_t size) +{ + return heapHelper_.AllocateSnapShotSpace(size); +} + +JSHandle ObjectFactory::NewJSNativeObject(void *externalPointer) +{ + return NewJSNativeObject(externalPointer, nullptr, nullptr); +} + +JSHandle ObjectFactory::NewJSNativeObject(void *externalPointer, DeleteEntryPoint callback, void *data) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass(env->GetJSNativeObjectClass()); + JSHandle nativeObject = JSHandle::Cast(NewJSObject(dynclass)); + JSHandle pointer = NewJSNativePointer(externalPointer, callback, data); + nativeObject->SetJSNativePointer(thread_, pointer.GetTaggedValue()); + vm_->PushToArrayDataList(*pointer); + return nativeObject; +} +} // namespace panda::ecmascript diff --git a/ecmascript/object_factory.h b/ecmascript/object_factory.h new file mode 100644 index 0000000000000000000000000000000000000000..87bef9e1ca926f1583d69b5599a9504415f52812 --- /dev/null +++ b/ecmascript/object_factory.h @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_OBJECT_FACTORY_H +#define PANDA_RUNTIME_ECMASCRIPT_OBJECT_FACTORY_H + +#include "ecmascript/base/error_type.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/js_function_kind.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_native_object.h" +#include "ecmascript/js_native_pointer.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/mem/ecma_heap_manager.h" +#include "ecmascript/mem/region_factory.h" +#include "ecmascript/object_wrapper.h" +#include "ecmascript/tagged_array.h" + +namespace panda::ecmascript { +class JSObject; +class JSArray; +class JSSymbol; +class JSFunctionBase; +class JSFunction; +class JSBoundFunction; +class JSProxyRevocFunction; +class JSAsyncAwaitStatusFunction; +class JSPrimitiveRef; +class GlobalEnv; +class GlobalEnvConstants; +class AccessorData; +class JSGlobalObject; +class LexicalEnv; +class JSDate; +class JSProxy; +class JSRealm; +class JSArguments; +class TaggedQueue; +class JSForInIterator; +class JSSet; +class JSMap; +class JSRegExp; +class JSSetIterator; +class JSMapIterator; +class JSArrayIterator; +class JSStringIterator; +class JSGeneratorObject; +class CompletionRecord; +class GeneratorContext; +class JSArrayBuffer; +class JSDataView; +class JSPromise; +class JSPromiseReactionsFunction; +class JSPromiseExecutorFunction; +class JSPromiseAllResolveElementFunction; +class PromiseReaction; +class PromiseCapability; +class PromiseIteratorRecord; +class JSAsyncFuncObject; +class JSAsyncFunction; +class PromiseRecord; +class JSLocale; +class ResolvingFunctionsRecord; +class JSFunctionExtraInfo; +class EcmaVM; +class Heap; +class ConstantPool; +class Program; +class EcmaModule; +class LayoutInfo; +class FreeObject; +class JSNativePointer; + +namespace job { +class MicroJobQueue; +class PendingJob; +} // namespace job +class TransitionHandler; +class PrototypeHandler; +class PropertyBox; +class ProtoChangeMarker; +class ProtoChangeDetails; +class ProfileTypeInfo; + +enum class PrimitiveType : uint8_t; +enum class IterationKind; + +using ErrorType = base::ErrorType; +using base::ErrorType; +using DeleteEntryPoint = void (*)(void *, void *); + +enum class RemoveSlots { YES, NO }; + +class ObjectFactory { +public: + explicit ObjectFactory(JSThread *thread, Heap *heap); + + JSHandle NewProfileTypeInfo(uint32_t length); + JSHandle NewConstantPool(uint32_t capacity); + JSHandle NewProgram(); + JSHandle NewEmptyEcmaModule(); + + JSHandle GetJSError(const ErrorType &errorType, const char *data = nullptr); + + JSHandle NewJSError(const ErrorType &errorType, const JSHandle &message); + + TransitionHandler *NewTransitionHandler(); + + PrototypeHandler *NewPrototypeHandler(); + + JSHandle NewEmptyJSObject(); + + // use for others create, prototype is Function.prototype + // use for native function + JSHandle NewJSFunction(const JSHandle &env, const void *nativeFunc = nullptr, + FunctionKind kind = FunctionKind::NORMAL_FUNCTION); + // use for method + JSHandle NewJSFunction(const JSHandle &env, JSMethod *method, + FunctionKind kind = FunctionKind::NORMAL_FUNCTION); + + JSHandle NewJSNativeErrorFunction(const JSHandle &env, const void *nativeFunc = nullptr); + + JSHandle NewSpecificTypedArrayFunction(const JSHandle &env, + const void *nativeFunc = nullptr); + + JSHandle OrdinaryNewJSObjectCreate(const JSHandle &proto); + + JSHandle NewJSBoundFunction(const JSHandle &target, + const JSHandle &boundThis, + const JSHandle &args); + + JSHandle NewJSProxyRevocFunction(const JSHandle &proxy, + const void *nativeFunc = nullptr); + + JSHandle NewJSAsyncAwaitStatusFunction(const void *nativeFunc = nullptr); + JSHandle NewJSGeneratorFunction(JSMethod *method); + + JSHandle NewAsyncFunction(JSMethod *method); + + JSHandle NewJSGeneratorObject(JSHandle generatorFunction); + + JSHandle NewJSAsyncFuncObject(); + + JSHandle NewJSPrimitiveRef(const JSHandle &function, + const JSHandle &object); + JSHandle NewJSPrimitiveRef(PrimitiveType type, const JSHandle &object); + + // get JSHClass for Ecma ClassLinker + JSHandle NewGlobalEnv(JSHClass *globalEnvClass); + + // get JSHClass for Ecma ClassLinker + JSHandle NewLexicalEnv(int numSlots); + + inline LexicalEnv *InlineNewLexicalEnv(int numSlots); + + JSHandle NewJSSymbol(); + + JSHandle NewPrivateSymbol(); + + JSHandle NewPrivateNameSymbol(const JSHandle &name); + + JSHandle NewWellKnownSymbol(const JSHandle &name); + + JSHandle NewPublicSymbol(const JSHandle &name); + + JSHandle NewSymbolWithTable(const JSHandle &name); + + JSHandle NewPrivateNameSymbolWithChar(const char *description); + + JSHandle NewWellKnownSymbolWithChar(const char *description); + + JSHandle NewPublicSymbolWithChar(const char *description); + + JSHandle NewSymbolWithTableWithChar(const char *description); + + JSHandle NewAccessorData(); + JSHandle NewInternalAccessor(void *setter, void *getter); + + JSHandle NewPromiseCapability(); + + JSHandle NewPromiseReaction(); + + JSHandle NewPromiseRecord(); + + JSHandle NewResolvingFunctionsRecord(); + + JSHandle NewPromiseIteratorRecord(const JSHandle &itor, + const JSHandle &done); + + JSHandle NewMicroJobQueue(); + + JSHandle NewPendingJob(const JSHandle &func, const JSHandle &argv); + + JSHandle NewFunctionExtraInfo(const JSHandle &callBack, + const JSHandle &data); + + JSHandle NewJSArray(); + + JSHandle NewJSProxy(const JSHandle &target, const JSHandle &handler); + JSHandle NewJSRealm(); + + JSHandle NewJSArguments(); + + JSHandle NewJSString(const JSHandle &str); + + JSHandle NewTaggedArray(array_size_t length, JSTaggedValue initVal = JSTaggedValue::Hole()); + JSHandle NewTaggedArray(array_size_t length, JSTaggedValue initVal, bool nonMovable); + JSHandle NewTaggedArray(array_size_t length, JSTaggedValue initVal, MemSpaceType spaceType); + JSHandle NewDictionaryArray(array_size_t length); + JSHandle NewJSForinIterator(const JSHandle &obj); + + JSHandle NewPropertyBox(const JSHandle &name); + + JSHandle NewProtoChangeMarker(); + + JSHandle NewProtoChangeDetails(); + + // use for copy properties keys's array to another array + JSHandle ExtendArray(const JSHandle &old, array_size_t length, + JSTaggedValue initVal = JSTaggedValue::Hole()); + JSHandle CopyPartArray(const JSHandle &old, array_size_t start, array_size_t end); + JSHandle CopyArray(const JSHandle &old, array_size_t oldLength, array_size_t newLength, + JSTaggedValue initVal = JSTaggedValue::Hole()); + JSHandle CloneProperties(const JSHandle &old); + JSHandle CloneProperties(const JSHandle &old, const JSHandle &env, + const JSHandle &obj, const JSHandle &constpool); + + JSHandle CreateLayoutInfo(int properties, JSTaggedValue initVal = JSTaggedValue::Hole()); + + JSHandle ExtendLayoutInfo(const JSHandle &old, int properties, + JSTaggedValue initVal = JSTaggedValue::Hole()); + + JSHandle CopyLayoutInfo(const JSHandle &old); + + JSHandle CopyAndReSort(const JSHandle &old, int end, int capacity); + + JSHandle NewFromString(const CString &data); + + JSHandle NewFromStdString(const std::string &data); + + JSHandle NewFromUtf8(const uint8_t *utf8Data, uint32_t utf8Len); + + JSHandle NewFromUtf16(const uint16_t *utf16Data, uint32_t utf16Len); + + JSHandle NewFromUtf8Literal(const uint8_t *utf8Data, uint32_t utf8Len); + + JSHandle NewFromUtf16Literal(const uint16_t *utf16Data, uint32_t utf16Len); + + JSHandle NewFromString(EcmaString *str); + + JSHandle ConcatFromString(const JSHandle &prefix, const JSHandle &suffix); + + JSHandle ConcatFromChar(const char *prefix, const char *suffix); + + JSHandle SubString(const JSHandle &src, uint32_t start, uint32_t utf16_length); + + JSHandle GetEmptyString() const; + + JSHandle EmptyArray() const; + + JSHandle NewObjectWrapper(const JSHandle &value); + + // start gc test + void SetTriggerGc(bool flag, bool triggerSemiGC = false); + + FreeObject *FillFreeObject(uintptr_t address, size_t size, RemoveSlots removeSlots = RemoveSlots::NO); + + TaggedObject *NewDynObject(const JSHandle &dynclass, int inobjPropCount = 0); + + TaggedObject *NewNonMovableDynObject(const JSHandle &dynclass, int inobjPropCount = 0); + + void InitializeExtraProperties(const JSHandle &dynclass, TaggedObject *obj, int inobjPropCount); + + JSHandle NewTaggedQueue(array_size_t length); + + JSHandle GetEmptyTaggedQueue() const; + + JSHandle NewJSSetIterator(const JSHandle &set, IterationKind kind); + + JSHandle NewJSMapIterator(const JSHandle &map, IterationKind kind); + + JSHandle NewJSArrayIterator(const JSHandle &array, IterationKind kind); + + JSHandle NewCompletionRecord(uint8_t type, JSHandle value); + + JSHandle NewGeneratorContext(); + + JSHandle CreateJSPromiseReactionsFunction(const void *nativeFunc); + + JSHandle CreateJSPromiseExecutorFunction(const void *nativeFunc); + + JSHandle NewJSPromiseAllResolveElementFunction(const void *nativeFunc); + + JSHandle CloneObjectLiteral(JSHandle object, const JSHandle &env, + const JSHandle &constpool); + JSHandle CloneObjectLiteral(JSHandle object); + JSHandle CloneArrayLiteral(JSHandle object); + JSHandle CloneJSFuction(JSHandle obj, FunctionKind kind); + + void NewJSArrayBufferData(const JSHandle &array, int32_t length); + + JSHandle NewJSArrayBuffer(int32_t length); + + JSHandle NewJSArrayBuffer(void *buffer, int32_t length, const DeleteEntryPoint &deleter, void *data, + bool share = false); + + JSHandle NewJSDataView(JSHandle buffer, int32_t offset, int32_t length); + + void NewJSRegExpByteCodeData(const JSHandle ®exp, void *buffer, size_t size); + + template + inline void NewJSIntlIcuData(const JSHandle &obj, const S &icu, const DeleteEntryPoint &callback); + + EcmaString *InternString(const JSHandle &key); + + inline JSHandle NewJSNativePointer(void *externalPointer, bool nonMovable = false); + inline JSHandle NewJSNativePointer(void *externalPointer, DeleteEntryPoint callBack, void *data, + bool nonMovable = false); + + JSHandle NewJSObjectByClass(const JSHandle &keys, const JSHandle &values); + JSHandle NewJSObjectByClass(const JSHandle &properties, size_t length); + + // only use for creating Function.prototype and Function + JSHandle NewJSFunctionByDynClass(JSMethod *method, const JSHandle &clazz, + FunctionKind kind = FunctionKind::NORMAL_FUNCTION); + EcmaString *ResolveString(uint32_t stringId); + + void ObtainRootClass(const JSHandle &globalEnv); + + const EcmaHeapManager &GetHeapManager() const + { + return heapHelper_; + } + + // used for creating jsobject by constructor + JSHandle NewJSObjectByConstructor(const JSHandle &constructor, + const JSHandle &newTarget); + + uintptr_t NewSpaceBySnapShotAllocator(size_t size); + JSHandle NewJSNativeObject(void *externalPointer); + JSHandle NewJSNativeObject(void *externalPointer, DeleteEntryPoint callback, void *data); + + ~ObjectFactory() = default; + +private: + friend class GlobalEnv; + friend class GlobalEnvConstants; + friend class EcmaString; + JSHandle NewJSFunctionImpl(JSMethod *method); + + void InitObjectFields(const TaggedObject *object); + + JSThread *thread_{nullptr}; + bool isTriggerGc_{false}; + bool triggerSemiGC_{false}; + EcmaHeapManager heapHelper_; + + JSHClass *hclassClass_{nullptr}; + JSHClass *stringClass_{nullptr}; + JSHClass *arrayClass_{nullptr}; + JSHClass *dictionaryClass_{nullptr}; + JSHClass *freeObjectWithNoneFieldClass_{nullptr}; + JSHClass *freeObjectWithOneFieldClass_{nullptr}; + JSHClass *freeObjectWithTwoFieldClass_{nullptr}; + + JSHClass *completionRecordClass_{nullptr}; + JSHClass *generatorContextClass_{nullptr}; + JSHClass *envClass_{nullptr}; + JSHClass *symbolClass_{nullptr}; + JSHClass *accessorDataClass_{nullptr}; + JSHClass *internalAccessorClass_{nullptr}; + JSHClass *capabilityRecordClass_{nullptr}; + JSHClass *reactionsRecordClass_{nullptr}; + JSHClass *promiseIteratorRecordClass_{nullptr}; + JSHClass *microJobQueueClass_{nullptr}; + JSHClass *pendingJobClass_{nullptr}; + JSHClass *jsProxyOrdinaryClass_{nullptr}; + JSHClass *jsProxyCallableClass_{nullptr}; + JSHClass *jsProxyConstructClass_{nullptr}; + JSHClass *objectWrapperClass_{nullptr}; + JSHClass *PropertyBoxClass_{nullptr}; + JSHClass *protoChangeDetailsClass_{nullptr}; + JSHClass *protoChangeMarkerClass_{nullptr}; + JSHClass *promiseRecordClass_{nullptr}; + JSHClass *promiseResolvingFunctionsRecord_{nullptr}; + JSHClass *jsNativePointerClass_{nullptr}; + JSHClass *transitionHandlerClass_{nullptr}; + JSHClass *prototypeHandlerClass_{nullptr}; + JSHClass *functionExtraInfo_{nullptr}; + JSHClass *jsRealmClass_{nullptr}; + JSHClass *programClass_{nullptr}; + JSHClass *ecmaModuleClass_{nullptr}; + + EcmaVM *vm_{nullptr}; + Heap *heap_{nullptr}; + + NO_COPY_SEMANTIC(ObjectFactory); + NO_MOVE_SEMANTIC(ObjectFactory); + + void NewObjectHook(); + + // used for creating jshclass in Builtins, Function, Class_Linker + JSHandle NewEcmaDynClass(uint32_t size, JSType type); + // used for creating jshclass in GlobalEnv, EcmaVM + JSHandle NewEcmaDynClass(JSHClass *hclass, uint32_t size, JSType type); + // used for creating jshclass in Builtins, Function, Class_Linker + JSHandle NewEcmaDynClass(uint32_t size, JSType type, const JSHandle &prototype); + + // used for creating Function + JSHandle NewJSObject(const JSHandle &jshclass); + + // used to create nonmovable js_object + JSHandle NewNonMovableJSObject(const JSHandle &jshclass); + + // used for creating Function + JSHandle NewJSFunction(const JSHandle &env, const JSHandle &dynKlass); + JSHandle CreateObjectClass(const JSHandle &keys, const JSHandle &values); + JSHandle CreateObjectClass(const JSHandle &properties, size_t length); + JSHandle CreateFunctionClass(FunctionKind kind, uint32_t size, JSType type, + const JSHandle &prototype); + + // used for creating ref.prototype in buildins, such as Number.prototype + JSHandle NewJSPrimitiveRef(const JSHandle &dynKlass, + const JSHandle &object); + + JSHandle GetStringFromStringTable(const uint8_t *utf8Data, uint32_t utf8Len) const; + // For MUtf-8 string data + EcmaString *GetRawStringFromStringTable(const uint8_t *mutf8Data, uint32_t utf16Len) const; + + JSHandle GetStringFromStringTable(const uint16_t *utf16Data, uint32_t utf16Len) const; + + JSHandle GetStringFromStringTable(EcmaString *string) const; + + inline EcmaString *AllocStringObject(size_t size); + inline EcmaString *AllocNonMovableStringObject(size_t size); + JSHandle NewEmptyArray(); // only used for EcmaVM. + + JSHandle CreateJSArguments(); + JSHandle CreateJSArrayInstanceClass(JSHandle proto); + JSHandle CreateJSRegExpInstanceClass(JSHandle proto); + + friend class Builtins; // create builtins object need dynclass + friend class JSFunction; // create prototype_or_dynclass need dynclass + friend class JSHClass; // HC transition need dynclass + friend class EcmaVM; // hold the factory instance + friend class JsVerificationTest; + friend class PandaFileTranslator; + friend class LiteralDataExtractor; +}; + +class ClassLinkerFactory { +private: + friend class GlobalEnv; // root class in class_linker need dynclass + friend class EcmaVM; // root class in class_linker need dynclass +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_OBJECT_FACTORY_H diff --git a/ecmascript/object_operator.cpp b/ecmascript/object_operator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a2ca2eb5bf5922e40554b00e5e1f056c07482480 --- /dev/null +++ b/ecmascript/object_operator.cpp @@ -0,0 +1,747 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "object_operator.h" +#include "ecma_vm.h" +#include "ecmascript/accessor_data.h" +#include "ecmascript/global_dictionary-inl.h" +#include "ecmascript/global_env.h" +#include "ecmascript/ic/property_box.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/mem/c_string.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_dictionary.h" +#include "global_dictionary.h" + +namespace panda::ecmascript { +void ObjectOperator::HandleKey(const JSHandle &key) +{ + if (key->IsInt()) { + int32_t keyInt = key->GetInt(); + if (keyInt >= 0) { + elementIndex_ = static_cast(keyInt); + return; + } + key_ = JSHandle::Cast(base::NumberHelper::NumberToString(thread_, JSTaggedValue(keyInt))); + return; + } + + if (key->IsString()) { + uint32_t index = 0; + if (JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index)) { + ASSERT(index < JSObject::MAX_ELEMENT_INDEX); + elementIndex_ = index; + return; + } + if (EcmaString::Cast(key->GetTaggedObject())->IsInternString()) { + key_ = key; + return; + } + key_ = JSHandle(thread_, thread_->GetEcmaVM()->GetFactory()->InternString(key)); + return; + } + + if (key->IsDouble()) { + double number = key->GetDouble(); + if (number >= 0 && number < JSObject::MAX_ELEMENT_INDEX) { + auto integer = static_cast(number); + if (integer == number) { + elementIndex_ = static_cast(number); + return; + } + } + key_ = JSHandle::Cast(base::NumberHelper::NumberToString(thread_, key.GetTaggedValue())); + return; + } + + if (key->IsSymbol()) { + key_ = key; + return; + } + + JSHandle keyHandle(thread_, JSTaggedValue::ToPrimitive(thread_, key, PREFER_STRING)); + if (key->IsSymbol()) { + key_ = keyHandle; + return; + } + key_ = JSHandle(thread_, + thread_->GetEcmaVM()->GetFactory()->InternString( + JSHandle::Cast(JSTaggedValue::ToString(thread_, keyHandle)))); +} + +void ObjectOperator::UpdateHolder() +{ + if (holder_->IsString() && + (IsElement() && elementIndex_ < EcmaString::Cast(holder_->GetTaggedObject())->GetLength())) { + holder_.Update(JSPrimitiveRef::StringCreate(thread_, holder_).GetTaggedValue()); + } else { + holder_.Update(JSTaggedValue::ToPrototypeOrObj(thread_, holder_).GetTaggedValue()); + } +} + +void ObjectOperator::StartLookUp(OperatorType type) +{ + UpdateHolder(); + + if (type == OperatorType::OWN) { + LookupPropertyInHolder(); + } else { + LookupProperty(); + } +} + +void ObjectOperator::StartGlobalLookUp(OperatorType type) +{ + UpdateHolder(); + + if (type == OperatorType::OWN) { + GlobalLookupPropertyInHolder(); + } else { + GlobalLookupProperty(); + } +} + +ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle &key, OperatorType type) + : thread_(thread), + holder_(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()), + receiver_(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()) +{ + HandleKey(key); + StartGlobalLookUp(type); +} + +ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle &holder, const JSHandle &key, + OperatorType type) + : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, holder.GetTaggedValue()) +{ + HandleKey(key); + StartLookUp(type); +} + +ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle &holder, + const JSHandle &key, OperatorType type) + : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, holder.GetTaggedValue()) +{ + HandleKey(key); + StartLookUp(type); +} + +ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle &holder, uint32_t index, + OperatorType type) + : thread_(thread), + holder_(thread, holder.GetTaggedValue()), + receiver_(thread, holder.GetTaggedValue()), + elementIndex_(index) +{ + StartLookUp(type); +} + +ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle &holder, + const JSHandle &receiver, const JSHandle &key, + OperatorType type) + : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, receiver.GetTaggedValue()) +{ + SetHasReceiver(true); + HandleKey(key); + StartLookUp(type); +} + +// op for fast path +ObjectOperator::ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, + OperatorType type) + : thread_(thread), holder_(thread, receiver), receiver_(thread, receiver), key_(thread, name) +{ + ASSERT(name.IsStringOrSymbol()); + StartLookUp(type); +} +JSHandle ObjectOperator::FastGetValue() +{ + ASSERT(IsFound() && !value_.IsEmpty()); + if (value_->IsPropertyBox()) { + value_.Update(PropertyBox::Cast(value_->GetTaggedObject())->GetValue()); + } + if (!IsAccessorDescriptor()) { + return value_; + } + AccessorData *accessor = AccessorData::Cast(value_->GetTaggedObject()); + ASSERT(!accessor->IsInternal()); + // 8. Return Call(getter, Receiver). + return JSHandle(thread_, JSObject::CallGetter(thread_, accessor, receiver_)); +} +ObjectOperator::ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, + const PropertyAttributes &attr) + : thread_(thread), receiver_(thread, receiver), key_(thread, name) +{ + SetAttr(attr); +} +void ObjectOperator::FastAdd(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, + const JSHandle &value, const PropertyAttributes &attr) +{ + ObjectOperator op(thread, receiver, name, attr); + op.AddPropertyInternal(value); +} + +void ObjectOperator::ToPropertyDescriptor(PropertyDescriptor &desc) const +{ + DISALLOW_GARBAGE_COLLECTION; + if (!IsFound()) { + return; + } + + if (!IsAccessorDescriptor()) { + desc.SetWritable(IsWritable()); + JSTaggedValue val = GetValue(); + desc.SetValue(JSHandle(thread_, val)); + } else { + AccessorData *accessor = AccessorData::Cast(GetValue().GetTaggedObject()); + + if (UNLIKELY(accessor->IsInternal())) { + desc.SetWritable(IsWritable()); + auto val = accessor->CallInternalGet(thread_, JSHandle::Cast(GetHolder())); + desc.SetValue(JSHandle(thread_, val)); + } else { + desc.SetGetter(JSHandle(thread_, accessor->GetGetter())); + desc.SetSetter(JSHandle(thread_, accessor->GetSetter())); + } + } + + desc.SetEnumerable(IsEnumerable()); + desc.SetConfigurable(IsConfigurable()); +} + +void ObjectOperator::GlobalLookupProperty() +{ + GlobalLookupPropertyInHolder(); + if (IsFound()) { + return; + } + JSTaggedValue proto = JSObject::Cast(holder_->GetTaggedObject())->GetPrototype(thread_); + if (!proto.IsHeapObject()) { + return; + } + holder_.Update(proto); + if (holder_->IsJSProxy()) { + return; + } + SetIsOnPrototype(true); + LookupProperty(); +} + +void ObjectOperator::LookupProperty() +{ + while (true) { + LookupPropertyInHolder(); + if (IsFound()) { + return; + } + + JSTaggedValue proto = holder_.GetObject()->GetPrototype(thread_); + if (!proto.IsHeapObject()) { + return; + } + + holder_.Update(proto); + if (holder_->IsJSProxy()) { + return; + } + + SetIsOnPrototype(true); + } +} + +void ObjectOperator::LookupGlobal(const JSHandle &obj) +{ + ASSERT(obj->IsJSGlobalObject()); + if (IsElement()) { + LookupElementInlinedProps(obj); + return; + } + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (array->GetLength() == 0) { + return; + } + GlobalDictionary *dict = GlobalDictionary::Cast(array); + int entry = dict->FindEntry(key_.GetTaggedValue()); + if (entry == -1) { + return; + } + JSTaggedValue value(dict->GetBox(entry)); + uint32_t attr = dict->GetAttributes(entry).GetValue(); + SetFound(entry, value, attr, true); +} + +void ObjectOperator::LookupPropertyInlinedProps(const JSHandle &obj) +{ + if (IsElement()) { + LookupElementInlinedProps(obj); + return; + } + + if (obj->IsJSGlobalObject()) { + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (array->GetLength() == 0) { + return; + } + + GlobalDictionary *dict = GlobalDictionary::Cast(array); + int entry = dict->FindEntry(key_.GetTaggedValue()); + if (entry == -1) { + return; + } + + JSTaggedValue value(dict->GetBox(entry)); + uint32_t attr = dict->GetAttributes(entry).GetValue(); + SetFound(entry, value, attr, true); + return; + } + + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + JSHClass *jshclass = obj->GetJSHClass(); + JSTaggedValue attrs = jshclass->GetAttributes(); + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject()); + int propsNumber = jshclass->GetPropertiesNumber(); + int entry = layoutInfo->FindElementWithCache(thread_, jshclass, key_.GetTaggedValue(), propsNumber); + if (entry == -1) { + return; + } + PropertyAttributes attr(layoutInfo->GetAttr(entry)); + ASSERT(entry == static_cast(attr.GetOffset())); + JSTaggedValue value; + if (attr.IsInlinedProps()) { + value = obj->GetPropertyInlinedProps(entry); + } else { + entry -= JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS; + value = array->Get(entry); + } + + SetFound(entry, value, attr.GetValue(), true); + return; + } + + NameDictionary *dict = NameDictionary::Cast(array); + int entry = dict->FindEntry(key_.GetTaggedValue()); + if (entry == -1) { + return; + } + + JSTaggedValue value = dict->GetValue(entry); + uint32_t attr = dict->GetAttributes(entry).GetValue(); + SetFound(entry, value, attr, false); +} + +void ObjectOperator::TransitionForAttributeChanged(const JSHandle &receiver, PropertyAttributes attr) +{ + if (IsElement()) { + uint32_t index = GetIndex(); + if (!receiver->GetJSHClass()->IsDictionaryElement()) { + JSObject::ElementsToDictionary(thread_, receiver); + auto dict = NumberDictionary::Cast(receiver->GetElements().GetTaggedObject()); + index = dict->FindEntry(JSTaggedValue(index)); + PropertyAttributes origin = dict->GetAttributes(index); + attr.SetDictionaryOrder(origin.GetDictionaryOrder()); + dict->SetAttributes(thread_, index, attr); + } else { + auto dict = NumberDictionary::Cast(receiver->GetElements().GetTaggedObject()); + dict->SetAttributes(thread_, index, attr); + } + // update found result + UpdateFound(index, attr.GetValue(), false, true); + } else if (receiver->IsJSGlobalObject()) { + JSHandle dictHandle(thread_, receiver->GetProperties()); + GlobalDictionary::InvalidatePropertyBox(thread_, dictHandle, GetIndex(), attr); + } else { + uint32_t index = GetIndex(); + if (!receiver->GetJSHClass()->IsDictionaryMode()) { + JSHandle dict(JSObject::TransitionToDictionary(thread_, receiver)); + index = dict->FindEntry(key_.GetTaggedValue()); + PropertyAttributes origin = dict->GetAttributes(index); + attr.SetDictionaryOrder(origin.GetDictionaryOrder()); + dict->SetAttributes(thread_, index, attr); + } else { + auto dict = NameDictionary::Cast(receiver->GetProperties().GetTaggedObject()); + dict->SetAttributes(thread_, index, attr); + } + // update found result + UpdateFound(index, attr.GetValue(), false, true); + } +} + +bool ObjectOperator::UpdateValueAndDetails(const JSHandle &receiver, const JSHandle &value, + PropertyAttributes attr, bool attrChanged) +{ + bool isInternalAccessor = IsAccessorDescriptor() && AccessorData::Cast(GetValue().GetHeapObject())->IsInternal(); + if (attrChanged) { + TransitionForAttributeChanged(receiver, attr); + } + return UpdateDataValue(receiver, value, isInternalAccessor); +} + +bool ObjectOperator::UpdateDataValue(const JSHandle &receiver, const JSHandle &value, + bool isInternalAccessor, bool mayThrow) +{ + if (IsElement()) { + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + if (!elements->IsDictionaryMode()) { + elements->Set(thread_, GetIndex(), value.GetTaggedValue()); + return true; + } + + NumberDictionary *dict = NumberDictionary::Cast(elements); + dict->UpdateValue(thread_, GetIndex(), value.GetTaggedValue()); + return true; + } + + if (receiver->IsJSGlobalObject()) { + // need update cell type ? + auto *dict = GlobalDictionary::Cast(receiver->GetProperties().GetTaggedObject()); + PropertyBox *cell = dict->GetBox(GetIndex()); + cell->SetValue(thread_, value.GetTaggedValue()); + return true; + } + + if (isInternalAccessor) { + auto accessor = AccessorData::Cast(GetValue().GetHeapObject()); + if (accessor->HasSetter()) { + return accessor->CallInternalSet(thread_, JSHandle(receiver), value, mayThrow); + } + } + + TaggedArray *properties = TaggedArray::Cast(receiver->GetProperties().GetTaggedObject()); + if (!properties->IsDictionaryMode()) { + PropertyAttributes attr = GetAttr(); + if (attr.IsInlinedProps()) { + receiver->SetPropertyInlinedProps(thread_, GetIndex(), value.GetTaggedValue()); + } else { + properties->Set(thread_, GetIndex(), value.GetTaggedValue()); + } + } else { + NameDictionary::Cast(properties)->UpdateValue(thread_, GetIndex(), value.GetTaggedValue()); + } + return true; +} + +bool ObjectOperator::WriteDataProperty(const JSHandle &receiver, const PropertyDescriptor &desc) +{ + PropertyAttributes attr = GetAttr(); + bool attrChanged = false; + + // composed new attribute from desc + if (desc.HasConfigurable() && attr.IsConfigurable() != desc.IsConfigurable()) { + attr.SetConfigurable(desc.IsConfigurable()); + attrChanged = true; + } + if (desc.HasEnumerable() && attr.IsEnumerable() != desc.IsEnumerable()) { + attr.SetEnumerable(desc.IsEnumerable()); + attrChanged = true; + } + + if (!desc.IsAccessorDescriptor()) { + if (desc.HasWritable() && attr.IsWritable() != desc.IsWritable()) { + attr.SetWritable(desc.IsWritable()); + attrChanged = true; + } + if (!desc.HasValue()) { + if (attrChanged) { + TransitionForAttributeChanged(receiver, attr); + } + return true; + } + + if (IsAccessorDescriptor()) { + auto accessor = AccessorData::Cast(GetValue().GetHeapObject()); + if (!accessor->IsInternal() || !accessor->HasSetter()) { + attr.SetIsAccessor(false); + attrChanged = true; + } + } + + return UpdateValueAndDetails(receiver, desc.GetValue(), attr, attrChanged); + } else { + if (IsAccessorDescriptor() && !IsElement()) { + TaggedArray *properties = TaggedArray::Cast(receiver->GetProperties().GetTaggedObject()); + if (!properties->IsDictionaryMode()) { + // as some accessorData is in globalEnv, we need to new accessorData. + JSHandle accessor = thread_->GetEcmaVM()->GetFactory()->NewAccessorData(); + + if (desc.HasGetter()) { + accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue()); + } else { + accessor->SetGetter(thread_, JSHandle::Cast(value_)->GetGetter()); + } + if (desc.HasSetter()) { + accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue()); + } else { + accessor->SetSetter(thread_, JSHandle::Cast(value_)->GetSetter()); + } + + JSHandle dict(JSObject::TransitionToDictionary(thread_, receiver)); + int entry = dict->FindEntry(key_.GetTaggedValue()); + ASSERT(entry != -1); + dict->UpdateValueAndAttributes(thread_, entry, accessor.GetTaggedValue(), attr); + return true; + } + } + + JSHandle accessor = IsAccessorDescriptor() + ? JSHandle::Cast(value_) + : thread_->GetEcmaVM()->GetFactory()->NewAccessorData(); + if (desc.HasGetter()) { + accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue()); + } + + if (desc.HasSetter()) { + accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue()); + } + + if (!IsAccessorDescriptor()) { + attr.SetIsAccessor(true); + attrChanged = true; + } + + JSHandle value = JSHandle::Cast(accessor); + return UpdateValueAndDetails(receiver, value, attr, attrChanged); + } +} + +void ObjectOperator::DeletePropertyInHolder() +{ + if (IsElement()) { + return DeleteElementInHolder(); + } + + JSObject::DeletePropertyInternal(thread_, JSHandle(holder_), key_, GetIndex()); +} + +bool ObjectOperator::AddProperty(const JSHandle &receiver, const JSHandle &value, + PropertyAttributes attr) +{ + if (IsElement()) { + return JSObject::AddElementInternal(thread_, receiver, elementIndex_, value, attr); + } + + ResetState(); + receiver_.Update(receiver.GetTaggedValue()); + SetAttr(attr.GetValue()); + AddPropertyInternal(value); + return true; +} + +void ObjectOperator::WriteElement(const JSHandle &receiver, JSTaggedValue value) const +{ + ASSERT(IsElement() && GetIndex() < JSObject::MAX_ELEMENT_INDEX); + + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + if (!elements->IsDictionaryMode()) { + elements->Set(thread_, index_, value); + receiver->GetJSHClass()->UpdateRepresentation(value); + return; + } + + NumberDictionary *dictionary = NumberDictionary::Cast(elements); + dictionary->UpdateValue(thread_, GetIndex(), value); +} + +void ObjectOperator::DeleteElementInHolder() const +{ + JSHandle obj(holder_); + + TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject()); + if (!elements->IsDictionaryMode()) { + elements->Set(thread_, index_, JSTaggedValue::Hole()); + JSObject::ElementsToDictionary(thread_, JSHandle(holder_)); + } else { + JSHandle dictHandle(thread_, elements); + NumberDictionary *dict = NumberDictionary::Remove(thread_, dictHandle, GetIndex()); + obj->SetElements(thread_, JSTaggedValue(dict)); + } +} + +void ObjectOperator::SetFound(uint32_t index, JSTaggedValue value, uint32_t attr, bool mode, bool transition) +{ + SetIndex(index); + SetValue(value); + SetFastMode(mode); + SetIsTransition(transition); + SetAttr(attr); +} + +void ObjectOperator::UpdateFound(uint32_t index, uint32_t attr, bool mode, bool transition) +{ + SetIndex(index); + SetFastMode(mode); + SetIsTransition(transition); + SetAttr(attr); +} + +void ObjectOperator::ResetState() +{ + // index may used by element + SetIndex(NOT_FOUND_INDEX); + SetValue(JSTaggedValue::Undefined()); + SetFastMode(false); + SetAttr(0); + SetIsOnPrototype(false); + SetHasReceiver(false); +} + +void ObjectOperator::LookupElementInlinedProps(const JSHandle &obj) +{ + // if is js string, do special. + if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(obj.GetTaggedValue().GetTaggedObject())->IsString()) { + PropertyDescriptor desc(thread_); + bool status = JSPrimitiveRef::StringGetIndexProperty(thread_, obj, elementIndex_, &desc); + if (status) { + PropertyAttributes attr(desc); + SetFound(elementIndex_, desc.GetValue().GetTaggedValue(), attr.GetValue(), true); + return; + } + } + { + DISALLOW_GARBAGE_COLLECTION; + TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject()); + if (elements->GetLength() == 0) { + return; // Empty Array + } + + if (!elements->IsDictionaryMode()) { + if (elements->GetLength() <= elementIndex_) { + return; + } + + JSTaggedValue value = elements->Get(elementIndex_); + if (value.IsHole()) { + return; + } + SetFound(elementIndex_, value, PropertyAttributes::GetDefaultAttributes(), true); + } else { + NumberDictionary *dictionary = NumberDictionary::Cast(obj->GetElements().GetTaggedObject()); + JSTaggedValue key(static_cast(elementIndex_)); + int entry = dictionary->FindEntry(key); + if (entry == -1) { + return; + } + + uint32_t attr = dictionary->GetAttributes(entry).GetValue(); + SetFound(entry, dictionary->GetValue(entry), attr, false); + } + } +} + +void ObjectOperator::AddPropertyInternal(const JSHandle &value) +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle obj(GetReceiver()); + PropertyAttributes attr = GetAttr(); + if (obj->IsJSGlobalObject()) { + JSMutableHandle dict(thread_, obj->GetProperties()); + if (dict->GetLength() == 0) { + dict.Update(JSTaggedValue(GlobalDictionary::Create(thread_))); + } + + // Add PropertyBox to global dictionary + JSHandle cellHandle = factory->NewPropertyBox(key_); + cellHandle->SetValue(thread_, value.GetTaggedValue()); + PropertyBoxType cellType = value->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT; + attr.SetBoxType(cellType); + + JSTaggedValue properties( + GlobalDictionary::PutIfAbsent(thread_, dict, key_, JSHandle(cellHandle), attr)); + obj->SetProperties(thread_, properties); + // index and fastMode is not essential for global obj; + SetFound(0, cellHandle.GetTaggedValue(), attr.GetValue(), true); + return; + } + + if (obj->IsJSArray() && key_.GetTaggedValue() == thread_->GlobalConstants()->GetConstructorString()) { + obj->GetJSHClass()->SetHasConstructor(true); + } + + uint32_t unusedInlinedProps = obj->GetJSHClass()->GetUnusedInlinedProps(); + if (unusedInlinedProps != 0) { + uint32_t order = JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS - unusedInlinedProps; + obj->SetPropertyInlinedProps(thread_, order, value.GetTaggedValue()); + attr.SetOffset(order); + attr.SetIsInlinedProps(true); + JSHClass::AddProperty(thread_, obj, key_, attr); + SetFound(order, value.GetTaggedValue(), attr.GetValue(), true, true); + return; + } + + JSMutableHandle array(thread_, obj->GetProperties()); + array_size_t length = array->GetLength(); + if (length == 0) { + length = JSObject::MIN_PROPERTIES_LENGTH; + array.Update(factory->NewTaggedArray(length).GetTaggedValue()); + obj->SetProperties(thread_, array.GetTaggedValue()); + } + + if (!array->IsDictionaryMode()) { + attr.SetIsInlinedProps(false); + + array_size_t nonInlinedProps = + JSHClass::DEFAULT_CAPACITY_OF_OUT_OBJECTS - obj->GetJSHClass()->GetUnusedNonInlinedProps(); + ASSERT(length >= nonInlinedProps); + // if array is full, grow array or change to dictionary mode + if (length == nonInlinedProps) { + if (length >= JSHClass::DEFAULT_CAPACITY_OF_OUT_OBJECTS) { + // change to dictionary and add one. + JSHandle dict(JSObject::TransitionToDictionary(thread_, obj)); + attr.SetDictionaryOrder(PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES); + auto result = JSTaggedValue(NameDictionary::PutIfAbsent(thread_, dict, key_, value, attr)); + obj->SetProperties(thread_, result); + // index is not essential when fastMode is false; + SetFound(0, value.GetTaggedValue(), attr.GetValue(), false); + return; + } + // Grow properties array size + array_size_t capacity = JSObject::ComputePropertyCapacity(length); + array.Update(factory->CopyArray(array, length, capacity).GetTaggedValue()); + obj->SetProperties(thread_, array.GetTaggedValue()); + } + + attr.SetOffset(nonInlinedProps + JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + JSHClass::AddProperty(thread_, obj, key_, attr); + array->Set(thread_, nonInlinedProps, value.GetTaggedValue()); + SetFound(nonInlinedProps, value.GetTaggedValue(), attr.GetValue(), true, true); + return; + } + + JSHandle dictHandle(array); + auto result = JSTaggedValue(NameDictionary::PutIfAbsent(thread_, dictHandle, key_, value, attr)); + obj->SetProperties(thread_, result); + SetFound(0, value.GetTaggedValue(), attr.GetValue(), false); +} + +void ObjectOperator::DefineSetter(const JSHandle &value) +{ + ASSERT(IsAccessorDescriptor()); + JSHandle accessor = JSHandle::Cast(value_); + accessor->SetSetter(thread_, value.GetTaggedValue()); + UpdateDataValue(JSHandle::Cast(receiver_), JSHandle::Cast(accessor), false); +} + +void ObjectOperator::DefineGetter(const JSHandle &value) +{ + ASSERT(IsAccessorDescriptor()); + JSHandle accessor = JSHandle::Cast(value_); + accessor->SetGetter(thread_, value.GetTaggedValue()); + UpdateDataValue(JSHandle::Cast(receiver_), JSHandle::Cast(accessor), false); +} +} // namespace panda::ecmascript diff --git a/ecmascript/object_operator.h b/ecmascript/object_operator.h new file mode 100644 index 0000000000000000000000000000000000000000..9dbffc0c68bae964c0b4e177401c13d5fd580de0 --- /dev/null +++ b/ecmascript/object_operator.h @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_OBJECT_OPERATOR_H +#define PANDA_RUNTIME_ECMASCRIPT_OBJECT_OPERATOR_H + +#include "ecmascript/js_handle.h" +#include "ecmascript/js_object.h" +#include "ecmascript/property_attributes.h" +#include "libpandabase/utils/bit_field.h" + +namespace panda::ecmascript { +class PropertyDescriptor; + +enum class OperatorType : uint8_t { + PROTOTYPE_CHAIN, + OWN, +}; + +class ObjectOperator final { +public: + explicit ObjectOperator() = default; + + explicit ObjectOperator(JSThread *thread, const JSHandle &key, + OperatorType type = OperatorType::PROTOTYPE_CHAIN); + + explicit ObjectOperator(JSThread *thread, const JSHandle &holder, const JSHandle &key, + OperatorType type = OperatorType::PROTOTYPE_CHAIN); + + explicit ObjectOperator(JSThread *thread, const JSHandle &holder, const JSHandle &key, + OperatorType type = OperatorType::PROTOTYPE_CHAIN); + + explicit ObjectOperator(JSThread *thread, const JSHandle &holder, + const JSHandle &receiver, const JSHandle &key, + OperatorType type = OperatorType::PROTOTYPE_CHAIN); + + explicit ObjectOperator(JSThread *thread, const JSHandle &holder, uint32_t index, + OperatorType type = OperatorType::PROTOTYPE_CHAIN); + // op for fast path, name can only string and symbol, and can't be number. + explicit ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, + OperatorType type = OperatorType::PROTOTYPE_CHAIN); + // op for fast add + explicit ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, + const PropertyAttributes &attr); + + static void FastAdd(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, + const JSHandle &value, const PropertyAttributes &attr); + + NO_COPY_SEMANTIC(ObjectOperator); + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(ObjectOperator); + ~ObjectOperator() = default; + + /** + * Create ObjectOperator instance by new operator is forbidden, for the member holder is a JSHandle type. it must + * be created and destroyed on stack + */ + void *operator new([[maybe_unused]] size_t t) = delete; + void operator delete([[maybe_unused]] void *ptr) = delete; + + inline bool IsFound() const + { + return index_ != NOT_FOUND_INDEX; + } + + inline bool IsFastMode() const + { + return IsFastModeField::Get(metaData_); + } + + inline void SetFastMode(bool flag) + { + IsFastModeField::Set(flag, &metaData_); + } + + inline bool IsElement() const + { + return key_.IsEmpty(); + } + + inline bool IsOnPrototype() const + { + return IsOnPrototypeField::Get(metaData_); + } + + inline void SetIsOnPrototype(bool flag) + { + IsOnPrototypeField::Set(flag, &metaData_); + } + + inline bool HasReceiver() const + { + return HasReceiverField::Get(metaData_); + } + + inline void SetHasReceiver(bool flag) + { + HasReceiverField::Set(flag, &metaData_); + } + + inline bool IsTransition() const + { + return IsTransitionField::Get(metaData_); + } + + inline void SetIsTransition(bool flag) + { + IsTransitionField::Set(flag, &metaData_); + } + + inline PropertyAttributes GetAttr() const + { + return attributes_; + } + + inline void SetAttr(uint32_t attr) + { + attributes_ = PropertyAttributes(attr); + } + + inline void SetAttr(const PropertyAttributes &attr) + { + attributes_ = PropertyAttributes(attr); + } + + inline bool IsPrimitiveAttr() const + { + return !attributes_.GetValue(); + } + + inline bool IsWritable() const + { + return GetAttr().IsWritable(); + } + + inline bool IsEnumerable() const + { + return GetAttr().IsEnumerable(); + } + + inline bool IsConfigurable() const + { + return GetAttr().IsConfigurable(); + } + + inline bool IsAccessorDescriptor() const + { + return GetAttr().IsAccessor(); + } + + inline bool IsInlinedProps() const + { + return GetAttr().IsInlinedProps(); + } + + inline JSTaggedValue GetValue() const + { + if (value_.IsEmpty()) { + return JSTaggedValue::Undefined(); + } + return value_.GetTaggedValue(); + } + + JSHandle FastGetValue(); + inline void SetValue(JSTaggedValue value) + { + if (value_.IsEmpty()) { + value_ = JSMutableHandle(thread_, value); + } + value_.Update(value); + } + + inline void SetIndex(uint32_t index) + { + index_ = index; + } + + inline uint32_t GetIndex() const + { + return index_; + } + + inline bool HasHolder() const + { + return !holder_.IsEmpty(); + } + + inline JSHandle GetHolder() const + { + return holder_; + } + + inline JSHandle GetReceiver() const + { + return receiver_; + } + + inline JSHandle GetKey() const + { + if (key_.IsEmpty()) { + return JSHandle(thread_, JSTaggedValue::Undefined()); + } + return key_; + } + + inline uint32_t GetElementIndex() const + { + return elementIndex_; + } + + inline JSThread *GetThread() const + { + return thread_; + } + + void ToPropertyDescriptor(PropertyDescriptor &desc) const; + void LookupProperty(); + void GlobalLookupProperty(); + inline void ReLookupPropertyInReceiver() + { + ResetState(); + return LookupPropertyInlinedProps(JSHandle(receiver_)); + } + inline void SetAsDefaultAttr() + { + SetFound(NOT_FOUND_INDEX, JSTaggedValue::Undefined(), PropertyAttributes::GetDefaultAttributes(), false, false); + } + bool UpdateDataValue(const JSHandle &receiver, const JSHandle &value, + bool isInternalAccessor, bool mayThrow = false); + bool WriteDataPropertyInHolder(const PropertyDescriptor &desc) + { + JSHandle receiver(holder_); + return WriteDataProperty(receiver, desc); + } + bool WriteDataProperty(const JSHandle &receiver, const PropertyDescriptor &desc); + bool AddProperty(const JSHandle &receiver, const JSHandle &value, PropertyAttributes attr); + inline bool AddPropertyInHolder(const JSHandle &value, PropertyAttributes attr) + { + JSHandle obj(holder_); + return AddProperty(obj, value, attr); + } + void DeletePropertyInHolder(); + static constexpr uint32_t NOT_FOUND_INDEX = std::numeric_limits::max(); + static JSTaggedValue ToHolder(const JSHandle &holder); + void AddPropertyInternal(const JSHandle &value); + void DefineSetter(const JSHandle &value); + void DefineGetter(const JSHandle &value); + +private: + static constexpr uint64_t ATTR_LENGTH = 5; + static constexpr uint64_t INDEX_LENGTH = 32; + + using IsFastModeField = BitField; + using IsOnPrototypeField = IsFastModeField::NextFlag; // 1: on prototype + using HasReceiverField = IsOnPrototypeField::NextFlag; + using IsTransitionField = HasReceiverField::NextFlag; + + void UpdateHolder(); + void StartLookUp(OperatorType type); + void StartGlobalLookUp(OperatorType type); + void HandleKey(const JSHandle &key); + uint32_t ComputeElementCapacity(uint32_t oldCapacity); + void SetFound(uint32_t index, JSTaggedValue value, uint32_t attr, bool mode, bool transition = false); + void UpdateFound(uint32_t index, uint32_t attr, bool mode, bool transition); + void ResetState(); + inline void LookupPropertyInHolder() + { + JSHandle obj(holder_); + LookupPropertyInlinedProps(obj); + } + inline void GlobalLookupPropertyInHolder() + { + JSHandle obj(holder_); + LookupGlobal(obj); + } + void LookupGlobal(const JSHandle &obj); + void LookupPropertyInlinedProps(const JSHandle &obj); + void LookupElementInlinedProps(const JSHandle &obj); + void WriteElement(const JSHandle &receiver, const PropertyDescriptor &desc); + void WriteElement(const JSHandle &receiver, JSTaggedValue value) const; + void DeleteElementInHolder() const; + bool UpdateValueAndDetails(const JSHandle &receiver, const JSHandle &value, + PropertyAttributes attr, bool attrChanged); + void TransitionForAttributeChanged(const JSHandle &receiver, PropertyAttributes attr); + JSThread *thread_{nullptr}; + JSMutableHandle value_{}; + JSMutableHandle holder_{}; + JSMutableHandle receiver_{}; + JSHandle key_{}; + uint32_t elementIndex_{NOT_FOUND_INDEX}; + uint32_t index_{NOT_FOUND_INDEX}; + PropertyAttributes attributes_; + uint32_t metaData_{0}; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_OBJECT_OPERATOR_H diff --git a/ecmascript/object_wrapper.h b/ecmascript/object_wrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..8cba82605bee52c13889b59210d1cf780662defa --- /dev/null +++ b/ecmascript/object_wrapper.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_WRAPPER_OBJECT_H +#define PANDA_RUNTIME_ECMASCRIPT_WRAPPER_OBJECT_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_hclass.h" + +namespace panda::ecmascript { +class ObjectWrapper : public TaggedObject { +public: + static ObjectWrapper *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static constexpr size_t VALUE_OFFSET = ObjectHeaderSize(); + ACCESSORS(Value, VALUE_OFFSET, SIZE) + + DECL_VISIT_OBJECT(VALUE_OFFSET, SIZE) +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_WRAPPER_OBJECT_H \ No newline at end of file diff --git a/ecmascript/property_attributes.h b/ecmascript/property_attributes.h new file mode 100644 index 0000000000000000000000000000000000000000..0dae6320a7fc42c3d7c12d78d23548544ecb8b2a --- /dev/null +++ b/ecmascript/property_attributes.h @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_PROPERTY_ATTRIBUTES_H +#define PANDA_RUNTIME_ECMASCRIPT_PROPERTY_ATTRIBUTES_H + +#include "ecmascript/js_tagged_value.h" +#include "utils/bit_field.h" + +namespace panda::ecmascript { +class PropertyDescriptor; + +enum class Representation { + NONE, + INT, + DOUBLE, + NUMBER, + OBJECT, + MIXED, +}; + +enum class PropertyBoxType { + // Meaningful when a property cell does not contain the hole. + UNDEFINED, // The PREMONOMORPHIC of property cells. + CONSTANT, // Cell has been assigned only once. + CONSTANTTYPE, // Cell has been assigned only one type. + MUTABLE, // Cell will no longer be tracked as constant. + + // Meaningful when a property cell contains the hole. + UNINITIALIZED = UNDEFINED, // Cell has never been initialized. + INVALIDATED = CONSTANT, // Cell has been deleted, invalidated or never existed. +}; + +class PropertyAttributes { +public: + explicit PropertyAttributes() = default; + ~PropertyAttributes() = default; + + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(PropertyAttributes); + DEFAULT_COPY_SEMANTIC(PropertyAttributes); + + explicit PropertyAttributes(uint32_t v) : value_(v) {} + explicit PropertyAttributes(int32_t v) : value_(static_cast(v)) {} + explicit PropertyAttributes(JSTaggedValue v) : value_(v.GetInt()) {} + explicit PropertyAttributes(const PropertyDescriptor &desc); + + static constexpr uint32_t DICTIONARY_ORDER_NUM = 20; + static constexpr uint32_t OFFSET_BITFIELD_NUM = 10; + static constexpr uint32_t MAX_CAPACITY_OF_PROPERTIES = (1U << OFFSET_BITFIELD_NUM); + + using PropertyMetaDataField = BitField; // 4: property metaData field occupies 4 bits + using AttributesField = BitField; // 4: attributes field occupies 4 bits + using DefaultAttributesField = BitField; // 3: default attributes field occupies 3 bits + using WritableField = BitField; // 1: writable field occupies 1 bits + using EnumerableField = WritableField::NextFlag; + using ConfigurableField = EnumerableField::NextFlag; + using IsAccessorField = ConfigurableField::NextFlag; // 4 + + // fast mode + using IsInlinedPropsField = PropertyMetaDataField::NextFlag; // 5 + using RepresentationField = IsInlinedPropsField::NextField; // 3: 3 bits, 6-8 + using OffsetField = RepresentationField::NextField; // 18 + + static constexpr uint32_t NORMAL_ATTR_BITS = 18; + using NormalAttrField = BitField; + using SortedIndexField = OffsetField::NextField; // 28 + + // dictionary mode, include global + using PropertyBoxTypeField = PropertyMetaDataField::NextField; // 2: 2 bits, 5-6 + using DictionaryOrderField = PropertyBoxTypeField::NextField; // 26 + + static constexpr uint32_t BIT_SIZE = 28; + static constexpr int INTIAL_PROPERTY_INDEX = 0; + + inline int GetPropertyMetaData() const + { + return PropertyMetaDataField::Get(value_); + } + + static PropertyAttributes Default() + { + return PropertyAttributes(GetDefaultAttributes()); + } + + static PropertyAttributes Default(bool w, bool e, bool c, bool isAccessor = false) + { + uint32_t value = WritableField::Encode(w) | EnumerableField::Encode(e) | ConfigurableField::Encode(c) | + IsAccessorField::Encode(isAccessor); + return PropertyAttributes(value); + } + + static PropertyAttributes DefaultAccessor(bool w, bool e, bool c) + { + uint32_t value = WritableField::Encode(w) | EnumerableField::Encode(e) | ConfigurableField::Encode(c) | + IsAccessorField::Encode(true); + return PropertyAttributes(value); + } + + inline void SetDefaultAttributes() + { + AttributesField::Set(DefaultAttributesField::Mask(), &value_); + } + + static inline int GetDefaultAttributes() + { + return DefaultAttributesField::Mask(); + } + + static inline Representation TaggedToRepresentation(JSTaggedValue value) + { + if (value.IsInt()) { + return Representation::INT; + } + if (value.IsDouble()) { + return Representation::DOUBLE; + } + + return Representation::OBJECT; + } + + static Representation UpdateRepresentation(Representation oldRep, JSTaggedValue value) + { + if (oldRep == Representation::MIXED) { + return oldRep; + } + + Representation newRep = TaggedToRepresentation(value); + if (oldRep == Representation::NONE) { + return newRep; + } + if (oldRep == newRep) { + return oldRep; + } + + switch (oldRep) { + case Representation::INT: + case Representation::DOUBLE: + case Representation::NUMBER: + if (newRep != Representation::OBJECT) { + return Representation::NUMBER; + } + return Representation::MIXED; + case Representation::OBJECT: + return Representation::MIXED; + default: + UNREACHABLE(); + } + } + + inline bool IsDefaultAttributes() const + { + return AttributesField::Get(value_) == static_cast(DefaultAttributesField::Mask()); + } + + inline void SetNoneAttributes() + { + AttributesField::Set(0U, &value_); + } + + inline bool IsNoneAttributes() const + { + return AttributesField::Get(value_) == 0; + } + + inline void SetWritable(bool flag) + { + WritableField::Set(flag, &value_); + } + inline bool IsWritable() const + { + return WritableField::Get(value_); + } + inline void SetEnumerable(bool flag) + { + EnumerableField::Set(flag, &value_); + } + inline bool IsEnumerable() const + { + return EnumerableField::Get(value_); + } + inline void SetConfigurable(bool flag) + { + ConfigurableField::Set(flag, &value_); + } + inline bool IsConfigurable() const + { + return ConfigurableField::Get(value_); + } + + inline void SetIsAccessor(bool flag) + { + IsAccessorField::Set(flag, &value_); + } + inline bool IsAccessor() const + { + return IsAccessorField::Get(value_); + } + + inline void SetIsInlinedProps(bool flag) + { + IsInlinedPropsField::Set(flag, &value_); + } + inline bool IsInlinedProps() const + { + return IsInlinedPropsField::Get(value_); + } + + inline void SetRepresentation(Representation representation) + { + RepresentationField::Set(representation, &value_); + } + inline Representation GetRepresentation() const + { + return RepresentationField::Get(value_); + } + + inline void SetDictionaryOrder(uint32_t order) + { + DictionaryOrderField::Set(order, &value_); + } + inline uint32_t GetDictionaryOrder() const + { + return DictionaryOrderField::Get(value_); + } + + inline void SetOffset(uint32_t offset) + { + OffsetField::Set(offset, &value_); + } + inline uint32_t GetOffset() const + { + return OffsetField::Get(value_); + } + + inline void SetSortedIndex(uint32_t sortedIndex) + { + SortedIndexField::Set(sortedIndex, &value_); + } + inline uint32_t GetSortedIndex() const + { + return SortedIndexField::Get(value_); + } + + inline void SetNormalAttr(uint32_t normalAttr) + { + NormalAttrField::Set(normalAttr, &value_); + } + + inline uint32_t GetNormalAttr() const + { + return NormalAttrField::Get(value_); + } + + inline JSTaggedValue GetNormalTagged() const + { + return JSTaggedValue(static_cast(GetNormalAttr())); + } + + inline uint32_t GetValue() const + { + return value_; + } + + inline void SetBoxType(PropertyBoxType cellType) + { + PropertyBoxTypeField::Set(cellType, &value_); + } + + inline PropertyBoxType GetBoxType() const + { + return PropertyBoxTypeField::Get(value_); + } + + inline static bool IsValidIndex(int index) + { + return DictionaryOrderField::IsValid(index); + } + + inline JSTaggedValue GetTaggedValue() const + { + return JSTaggedValue(static_cast(value_)); + } + +private: + uint32_t value_{0}; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_PROPERTY_ATTRIBUTES_H diff --git a/ecmascript/record.h b/ecmascript/record.h new file mode 100644 index 0000000000000000000000000000000000000000..e2defb0d51b6fbec3b35b936b4c4572f11235a37 --- /dev/null +++ b/ecmascript/record.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_RECORD_H +#define PANDA_RUNTIME_ECMASCRIPT_RECORD_H + +#include "ecmascript/mem/tagged_object.h" + +namespace panda::ecmascript { +class Record : public TaggedObject { +public: + static constexpr size_t SIZE = sizeof(TaggedObject); +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_RECORD_H \ No newline at end of file diff --git a/ecmascript/regexp/dyn_chunk.cpp b/ecmascript/regexp/dyn_chunk.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11733aefa320c8be74d62e50234377a8ff3909b3 --- /dev/null +++ b/ecmascript/regexp/dyn_chunk.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/regexp/dyn_chunk.h" +#include "securec.h" + +namespace panda::ecmascript { +int DynChunk::Expand(size_t newSize) +{ + if (newSize > allocatedSize_) { + if (error_) { + return FAILURE; + } + ASSERT(allocatedSize_ <= std::numeric_limits::max() / ALLOCATE_MULTIPLIER); + size_t size = allocatedSize_ * ALLOCATE_MULTIPLIER; + if (size > newSize) { + newSize = size; + } + newSize = std::max(newSize, ALLOCATE_MIN_SIZE); + auto *newBuf = chunk_->NewArray(newSize); + if (newBuf == nullptr) { + error_ = true; + return FAILURE; + } + if (memset_s(newBuf, newSize, 0, newSize) != EOK) { + error_ = true; + return FAILURE; + } + if (buf_ != nullptr) { + if (memcpy_s(newBuf, size_, buf_, size_) != EOK) { + error_ = true; + return FAILURE; + } + } + buf_ = newBuf; + allocatedSize_ = newSize; + } + return SUCCESS; +} + +int DynChunk::Insert(uint32_t position, size_t len) +{ + if (size_ < position) { + return FAILURE; + } + if (Expand(size_ + len) != 0) { + return FAILURE; + } + size_t moveSize = size_ - position; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memmove_s(buf_ + position + len, moveSize, buf_ + position, moveSize) != EOK) { + return FAILURE; + } + size_ += len; + return SUCCESS; +} + +int DynChunk::Emit(const uint8_t *data, size_t length) +{ + if (UNLIKELY((size_ + length) > allocatedSize_)) { + if (Expand(size_ + length) != 0) { + return FAILURE; + } + } + + if (memcpy_s(buf_ + size_, // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + length, data, length) != EOK) { + return FAILURE; + } + size_ += length; + return SUCCESS; +} + +int DynChunk::EmitChar(uint8_t c) +{ + return Emit(&c, 1); +} + +int DynChunk::EmitSelf(size_t offset, size_t length) +{ + if (UNLIKELY((size_ + length) > allocatedSize_)) { + if (Expand(size_ + length) != 0) { + return FAILURE; + } + } + + if (memcpy_s(buf_ + size_, // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + length, + buf_ + offset, // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + length) != EOK) { + return FAILURE; + } + size_ += length; + return SUCCESS; +} + +int DynChunk::EmitStr(const char *str) +{ + return Emit(reinterpret_cast(str), strlen(str) + 1); +} +} // namespace panda::ecmascript diff --git a/ecmascript/regexp/dyn_chunk.h b/ecmascript/regexp/dyn_chunk.h new file mode 100644 index 0000000000000000000000000000000000000000..e0d43c56ba02a08cb50a2c366a3ba681aa98ba94 --- /dev/null +++ b/ecmascript/regexp/dyn_chunk.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_DYN_BUFFER_H +#define PANDA_RUNTIME_ECMASCRIPT_DYN_BUFFER_H + +#include +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/chunk.h" + +namespace panda::ecmascript { +class DynChunk { +public: + static constexpr size_t ALLOCATE_MIN_SIZE = 64; + static constexpr int FAILURE = -1; + static constexpr int SUCCESS = 0; + explicit DynChunk(Chunk *chunk) : chunk_(chunk) + { + ASSERT(chunk_ != nullptr); + }; + + ~DynChunk() = default; + + NO_COPY_SEMANTIC(DynChunk); + NO_MOVE_SEMANTIC(DynChunk); + + int Expand(size_t newSize); + + int Insert(uint32_t position, size_t len); + + int Emit(const uint8_t *data, size_t len); + + int EmitSelf(size_t offset, size_t len); + + int EmitChar(uint8_t c); + + int EmitStr(const char *str); + + inline int EmitU16(uint16_t data) + { + return Emit(reinterpret_cast(&data), U16_SIZE); + } + + inline int EmitU32(uint32_t data) + { + return Emit(reinterpret_cast(&data), U32_SIZE); + } + + inline int EmitU64(uint64_t data) + { + return Emit(reinterpret_cast(&data), U64_SIZE); + } + + inline void SetError() + { + error_ = true; + } + + inline size_t GetSize() const + { + return size_; + } + + inline size_t GetAllocatedSize() const + { + return allocatedSize_; + } + + inline bool GetError() const + { + return error_; + } + + inline uint32_t GetU32(size_t offset) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return *reinterpret_cast(buf_ + offset); + } + + inline void PutU32(size_t offset, uint32_t data) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *reinterpret_cast(buf_ + offset) = data; + } + + inline uint32_t GetU16(size_t offset) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return *reinterpret_cast(buf_ + offset); + } + + inline void PutU16(size_t offset, uint16_t data) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *reinterpret_cast(buf_ + offset) = data; + } + + inline uint32_t GetU8(size_t offset) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return *(buf_ + offset); + } + + inline void PutU8(size_t offset, uint8_t data) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *(buf_ + offset) = data; + } + + ALWAYS_INLINE static inline constexpr uint32_t GetBufferOffset() + { + return MEMBER_OFFSET(DynChunk, buf_); + } + +private: + static constexpr size_t ALLOCATE_MULTIPLIER = 2; + static constexpr size_t U16_SIZE = 2; + static constexpr size_t U32_SIZE = 4; + static constexpr size_t U64_SIZE = 8; + friend class RegExpParser; + friend class RegExpOpCode; + friend class RegExpExecutor; + + DynChunk(uint8_t *buf, Chunk *chunk) : buf_(buf), chunk_(chunk) + { + ASSERT(chunk_ != nullptr); + }; + + uint8_t *buf_ {nullptr}; + size_t size_ {0}; + size_t allocatedSize_ {0}; + bool error_ {false}; + Chunk *chunk_ {nullptr}; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_DYN_BUFFER_H diff --git a/ecmascript/regexp/regexp_executor.cpp b/ecmascript/regexp/regexp_executor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..930b72f455e8773f4e878b21a17ecd06499a072f --- /dev/null +++ b/ecmascript/regexp/regexp_executor.cpp @@ -0,0 +1,731 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/regexp/regexp_executor.h" + +#include "ecmascript/base/string_helper.h" +#include "ecmascript/regexp/dyn_chunk.h" +#include "ecmascript/regexp/regexp_opcode.h" +#include "securec.h" + +namespace panda::ecmascript { +using RegExpState = RegExpExecutor::RegExpState; +using MatchResult = RegExpExecutor::MatchResult; +bool RegExpExecutor::Execute(const uint8_t *input, uint32_t lastIndex, uint32_t length, uint8_t *buf, bool isWideChar) +{ + DynChunk buffer(buf, chunk_); + input_ = const_cast(input); + inputEnd_ = const_cast(input + length * (isWideChar ? WIDE_CHAR_SIZE : CHAR_SIZE)); + uint32_t size = buffer.GetU32(0); + nCapture_ = buffer.GetU32(RegExpParser::NUM_CAPTURE__OFFSET); + nStack_ = buffer.GetU32(RegExpParser::NUM_STACK_OFFSET); + flags_ = buffer.GetU32(RegExpParser::FLAGS_OFFSET); + isWideChar_ = isWideChar; + + uint32_t captureResultSize = sizeof(CaptureState) * nCapture_; + uint32_t stackSize = sizeof(uintptr_t) * nStack_; + stateSize_ = sizeof(RegExpState) + captureResultSize + stackSize; + stateStackLen_ = 0; + + if (captureResultSize != 0) { + captureResultList_ = chunk_->NewArray(nCapture_); + if (memset_s(captureResultList_, captureResultSize, 0, captureResultSize) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + } + if (stackSize != 0) { + stack_ = chunk_->NewArray(nStack_); + if (memset_s(stack_, stackSize, 0, stackSize) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + SetCurrentPtr(input + lastIndex * (isWideChar ? WIDE_CHAR_SIZE : CHAR_SIZE)); + SetCurrentPC(RegExpParser::OP_START_OFFSET); + + // first split + if ((flags_ & RegExpParser::FLAG_STICKY) == 0) { + PushRegExpState(STATE_SPLIT, RegExpParser::OP_START_OFFSET); + } + return ExecuteInternal(buffer, size); +} + +bool RegExpExecutor::MatchFailed(bool isMatched) +{ + while (true) { + if (stateStackLen_ == 0) { + return true; + } + RegExpState *state = PeekRegExpState(); + if (state->type_ == StateType::STATE_SPLIT) { + if (!isMatched) { + PopRegExpState(); + return false; + } + } else { + isMatched = (state->type_ == StateType::STATE_MATCH_AHEAD && isMatched) || + (state->type_ == StateType::STATE_NEGATIVE_MATCH_AHEAD && !isMatched); + if (isMatched) { + if (state->type_ == StateType::STATE_MATCH_AHEAD) { + PopRegExpState(false); + return false; + } + if (state->type_ == StateType::STATE_NEGATIVE_MATCH_AHEAD) { + PopRegExpState(); + return false; + } + } + } + DropRegExpState(); + } + + return true; +} + +bool RegExpExecutor::HandleFirstSplit() +{ + if (GetCurrentPC() == RegExpParser::OP_START_OFFSET && stateStackLen_ == 0 && + (flags_ & RegExpParser::FLAG_STICKY) == 0) { + if (IsEOF()) { + if (MatchFailed()) { + return false; + } + } else { + AdvanceCurrentPtr(); + PushRegExpState(STATE_SPLIT, RegExpParser::OP_START_OFFSET); + } + } + return true; +} + +bool RegExpExecutor::HandleOpAll(uint8_t opCode) +{ + if (IsEOF()) { + return !MatchFailed(); + } + uint32_t currentChar = GetCurrentChar(); + if ((opCode == RegExpOpCode::OP_DOTS) && IsTerminator(currentChar)) { + return !MatchFailed(); + } + Advance(opCode); + return true; +} + +bool RegExpExecutor::HandleOpChar(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t expectedChar; + if (opCode == RegExpOpCode::OP_CHAR32) { + expectedChar = byteCode.GetU32(GetCurrentPC() + 1); + } else { + expectedChar = byteCode.GetU16(GetCurrentPC() + 1); + } + if (IsEOF()) { + return !MatchFailed(); + } + uint32_t currentChar = GetCurrentChar(); + if (IsIgnoreCase()) { + currentChar = RegExpParser::Canonicalize(currentChar, IsUtf16()); + } + if (currentChar == expectedChar) { + Advance(opCode); + } else { + if (MatchFailed()) { + return false; + } + } + return true; +} + +bool RegExpExecutor::HandleOpWordBoundary(uint8_t opCode) +{ + if (IsEOF()) { + if (opCode == RegExpOpCode::OP_WORD_BOUNDARY) { + Advance(opCode); + } else { + if (MatchFailed()) { + return false; + } + } + return true; + } + bool preIsWord = false; + if (GetCurrentPtr() != input_) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + preIsWord = IsWordChar(PeekPrevChar(currentPtr_, input_)); + } + bool currentIsWord = IsWordChar(PeekChar(currentPtr_, inputEnd_)); + if (((opCode == RegExpOpCode::OP_WORD_BOUNDARY) && + ((!preIsWord && currentIsWord) || (preIsWord && !currentIsWord))) || + ((opCode == RegExpOpCode::OP_NOT_WORD_BOUNDARY) && + ((preIsWord && currentIsWord) || (!preIsWord && !currentIsWord)))) { + Advance(opCode); + } else { + if (MatchFailed()) { + return false; + } + } + return true; +} + +bool RegExpExecutor::HandleOpLineStart(uint8_t opCode) +{ + if (IsEOF()) { + return !MatchFailed(); + } + if ((GetCurrentPtr() == input_) || + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + ((flags_ & RegExpParser::FLAG_MULTILINE) != 0 && PeekPrevChar(currentPtr_, input_) == '\n')) { + Advance(opCode); + } else { + if (MatchFailed()) { + return false; + } + } + return true; +} + +bool RegExpExecutor::HandleOpLineEnd(uint8_t opCode) +{ + if (IsEOF() || + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + ((flags_ & RegExpParser::FLAG_MULTILINE) != 0 && PeekChar(currentPtr_, inputEnd_) == '\n')) { + Advance(opCode); + } else { + if (MatchFailed()) { + return false; + } + } + return true; +} + +void RegExpExecutor::HandleOpSaveStart(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t captureIndex = byteCode.GetU8(GetCurrentPC() + 1); + ASSERT(captureIndex < nCapture_); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + CaptureState *captureState = &captureResultList_[captureIndex]; + captureState->captureStart = GetCurrentPtr(); + Advance(opCode); +} + +void RegExpExecutor::HandleOpSaveEnd(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t captureIndex = byteCode.GetU8(GetCurrentPC() + 1); + ASSERT(captureIndex < nCapture_); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + CaptureState *captureState = &captureResultList_[captureIndex]; + captureState->captureEnd = GetCurrentPtr(); + Advance(opCode); +} + +void RegExpExecutor::HandleOpSaveReset(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t catpureStartIndex = byteCode.GetU8(GetCurrentPC() + SAVE_RESET_START); + uint32_t catpureEndIndex = byteCode.GetU8(GetCurrentPC() + SAVE_RESET_END); + for (uint32_t i = catpureStartIndex; i <= catpureEndIndex; i++) { + CaptureState *captureState = + &captureResultList_[i]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + captureState->captureStart = nullptr; + captureState->captureEnd = nullptr; + } + Advance(opCode); +} + +void RegExpExecutor::HandleOpMatch(const DynChunk &byteCode, uint8_t opCode) +{ + auto type = static_cast(opCode - RegExpOpCode::OP_SPLIT_NEXT); + ASSERT(type == STATE_SPLIT || type == STATE_MATCH_AHEAD || type == STATE_NEGATIVE_MATCH_AHEAD); + uint32_t offset = byteCode.GetU32(GetCurrentPC() + 1); + Advance(opCode); + uint32_t splitPc = GetCurrentPC() + offset; + PushRegExpState(type, splitPc); +} + +void RegExpExecutor::HandleOpSplitFirst(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t offset = byteCode.GetU32(GetCurrentPC() + 1); + Advance(opCode); + PushRegExpState(STATE_SPLIT, GetCurrentPC()); + AdvanceOffset(offset); +} + +bool RegExpExecutor::HandleOpPrev(uint8_t opCode) +{ + if (GetCurrentPtr() == input_) { + if (MatchFailed()) { + return false; + } + } else { + PrevPtr(¤tPtr_, input_); + Advance(opCode); + } + return true; +} + +void RegExpExecutor::HandleOpLoop(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t quantifyMin = byteCode.GetU32(GetCurrentPC() + LOOP_MIN_OFFSET); + uint32_t quantifyMax = byteCode.GetU32(GetCurrentPC() + LOOP_MAX_OFFSET); + uint32_t pcOffset = byteCode.GetU32(GetCurrentPC() + LOOP_PC_OFFSET); + Advance(opCode); + uint32_t loopPcEnd = GetCurrentPC(); + uint32_t loopPcStart = GetCurrentPC() + pcOffset; + bool isGreedy = opCode == RegExpOpCode::OP_LOOP_GREEDY; + uint32_t loopMax = isGreedy ? quantifyMax : quantifyMin; + + uint32_t loopCount = PeekStack(); + SetStackValue(++loopCount); + if (loopCount < loopMax) { + // greedy failed, goto next + if (loopCount >= quantifyMin) { + PushRegExpState(STATE_SPLIT, loopPcEnd); + } + // Goto loop start + SetCurrentPC(loopPcStart); + } else { + if (!isGreedy && (loopCount < quantifyMax)) { + PushRegExpState(STATE_SPLIT, loopPcStart); + } + } +} + +bool RegExpExecutor::HandleOpRange32(const DynChunk &byteCode) +{ + if (IsEOF()) { + return !MatchFailed(); + } + uint32_t currentChar = GetCurrentChar(); + if (IsIgnoreCase()) { + currentChar = RegExpParser::Canonicalize(currentChar, IsUtf16()); + } + uint16_t rangeCount = byteCode.GetU16(GetCurrentPC() + 1); + bool isFound = false; + int32_t idxMin = 0; + int32_t idxMax = rangeCount - 1; + int32_t idx = 0; + uint32_t low = 0; + uint32_t high = + byteCode.GetU32(GetCurrentPC() + RANGE32_HEAD_OFFSET + idxMax * RANGE32_MAX_OFFSET + RANGE32_MAX_HALF_OFFSET); + if (currentChar <= high) { + while (idxMin <= idxMax) { + idx = (idxMin + idxMax) / RANGE32_OFFSET; + low = byteCode.GetU32(GetCurrentPC() + RANGE32_HEAD_OFFSET + idx * RANGE32_MAX_OFFSET); + high = byteCode.GetU32(GetCurrentPC() + RANGE32_HEAD_OFFSET + idx * RANGE32_MAX_OFFSET + + RANGE32_MAX_HALF_OFFSET); + if (currentChar < low) { + idxMax = idx - 1; + } else if (currentChar > high) { + idxMin = idx + 1; + } else { + isFound = true; + break; + } + } + } + if (isFound) { + AdvanceOffset(rangeCount * RANGE32_MAX_OFFSET + RANGE32_HEAD_OFFSET); + } else { + if (MatchFailed()) { + return false; + } + } + return true; +} + +bool RegExpExecutor::HandleOpRange(const DynChunk &byteCode) +{ + if (IsEOF()) { + return !MatchFailed(); + } + uint32_t currentChar = GetCurrentChar(); + if (IsIgnoreCase()) { + currentChar = RegExpParser::Canonicalize(currentChar, IsUtf16()); + } + uint16_t rangeCount = byteCode.GetU16(GetCurrentPC() + 1); + bool isFound = false; + int32_t idxMin = 0; + int32_t idxMax = rangeCount - 1; + int32_t idx = 0; + uint32_t low = 0; + uint32_t high = + byteCode.GetU16(GetCurrentPC() + RANGE32_HEAD_OFFSET + idxMax * RANGE32_MAX_HALF_OFFSET + RANGE32_OFFSET); + if (currentChar <= high) { + while (idxMin <= idxMax) { + idx = (idxMin + idxMax) / RANGE32_OFFSET; + low = byteCode.GetU16(GetCurrentPC() + RANGE32_HEAD_OFFSET + idx * RANGE32_MAX_HALF_OFFSET); + high = + byteCode.GetU16(GetCurrentPC() + RANGE32_HEAD_OFFSET + idx * RANGE32_MAX_HALF_OFFSET + RANGE32_OFFSET); + if (currentChar < low) { + idxMax = idx - 1; + } else if (currentChar > high) { + idxMin = idx + 1; + } else { + isFound = true; + break; + } + } + } + if (isFound) { + AdvanceOffset(rangeCount * RANGE32_MAX_HALF_OFFSET + RANGE32_HEAD_OFFSET); + } else { + if (MatchFailed()) { + return false; + } + } + return true; +} + +bool RegExpExecutor::HandleOpBackReference(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t captureIndex = byteCode.GetU8(GetCurrentPC() + 1); + if (captureIndex >= nCapture_) { + return !MatchFailed(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const uint8_t *captureStart = captureResultList_[captureIndex].captureStart; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const uint8_t *captureEnd = captureResultList_[captureIndex].captureEnd; + if (captureStart == nullptr || captureEnd == nullptr) { + Advance(opCode); + return true; + } + bool isMatched = true; + if (opCode == RegExpOpCode::OP_BACKREFERENCE) { + const uint8_t *refCptr = captureStart; + while (refCptr < captureEnd) { + if (IsEOF()) { + isMatched = false; + break; + } + // NOLINTNEXTLINE(readability-identifier-naming) + uint32_t c1 = GetChar(&refCptr, captureEnd); + // NOLINTNEXTLINE(readability-identifier-naming) + uint32_t c2 = GetChar(¤tPtr_, inputEnd_); + if (IsIgnoreCase()) { + c1 = RegExpParser::Canonicalize(c1, IsUtf16()); + c2 = RegExpParser::Canonicalize(c2, IsUtf16()); + } + if (c1 != c2) { + isMatched = false; + break; + } + } + if (!isMatched) { + if (MatchFailed()) { + return false; + } + } else { + Advance(opCode); + } + } else { + const uint8_t *refCptr = captureEnd; + while (refCptr > captureStart) { + if (GetCurrentPtr() == input_) { + isMatched = false; + break; + } + // NOLINTNEXTLINE(readability-identifier-naming) + uint32_t c1 = GetPrevChar(&refCptr, captureStart); + // NOLINTNEXTLINE(readability-identifier-naming) + uint32_t c2 = GetPrevChar(¤tPtr_, input_); + if (IsIgnoreCase()) { + c1 = RegExpParser::Canonicalize(c1, IsUtf16()); + c2 = RegExpParser::Canonicalize(c2, IsUtf16()); + } + if (c1 != c2) { + isMatched = false; + break; + } + } + if (!isMatched) { + if (MatchFailed()) { + return false; + } + } else { + Advance(opCode); + } + } + return true; +} + +// NOLINTNEXTLINE(readability-function-size) +bool RegExpExecutor::ExecuteInternal(const DynChunk &byteCode, uint32_t pcEnd) +{ + while (GetCurrentPC() < pcEnd) { + // first split + if (!HandleFirstSplit()) { + return false; + } + uint8_t opCode = byteCode.GetU8(GetCurrentPC()); + switch (opCode) { + case RegExpOpCode::OP_DOTS: + case RegExpOpCode::OP_ALL: + if (!HandleOpAll(opCode)) { + return false; + } + break; + case RegExpOpCode::OP_CHAR32: + case RegExpOpCode::OP_CHAR: + if (!HandleOpChar(byteCode, opCode)) { + return false; + } + break; + case RegExpOpCode::OP_NOT_WORD_BOUNDARY: + case RegExpOpCode::OP_WORD_BOUNDARY: + if (!HandleOpWordBoundary(opCode)) { + return false; + } + break; + case RegExpOpCode::OP_LINE_START: + if (!HandleOpLineStart(opCode)) { + return false; + } + break; + case RegExpOpCode::OP_LINE_END: + if (!HandleOpLineEnd(opCode)) { + return false; + } + break; + case RegExpOpCode::OP_SAVE_START: + HandleOpSaveStart(byteCode, opCode); + break; + case RegExpOpCode::OP_SAVE_END: + HandleOpSaveEnd(byteCode, opCode); + break; + case RegExpOpCode::OP_GOTO: { + uint32_t offset = byteCode.GetU32(GetCurrentPC() + 1); + Advance(opCode, offset); + } break; + case RegExpOpCode::OP_MATCH: { + // jump to match ahead + if (MatchFailed(true)) { + return false; + } + } break; + case RegExpOpCode::OP_MATCH_END: { + return true; + } break; + case RegExpOpCode::OP_SAVE_RESET: + HandleOpSaveReset(byteCode, opCode); + break; + case RegExpOpCode::OP_SPLIT_NEXT: + case RegExpOpCode::OP_MATCH_AHEAD: + case RegExpOpCode::OP_NEGATIVE_MATCH_AHEAD: + HandleOpMatch(byteCode, opCode); + break; + case RegExpOpCode::OP_SPLIT_FIRST: + HandleOpSplitFirst(byteCode, opCode); + break; + case RegExpOpCode::OP_PREV: + if (!HandleOpPrev(opCode)) { + return false; + } + break; + case RegExpOpCode::OP_LOOP_GREEDY: + case RegExpOpCode::OP_LOOP: + HandleOpLoop(byteCode, opCode); + break; + case RegExpOpCode::OP_PUSH_CHAR: { + PushStack(reinterpret_cast(GetCurrentPtr())); + Advance(opCode); + } break; + case RegExpOpCode::OP_CHECK_CHAR: { + if (PopStack() != reinterpret_cast(GetCurrentPtr())) { + Advance(opCode); + } else { + uint32_t offset = byteCode.GetU32(GetCurrentPC() + 1); + Advance(opCode, offset); + } + } break; + case RegExpOpCode::OP_PUSH: { + PushStack(0); + Advance(opCode); + } break; + case RegExpOpCode::OP_POP: { + PopStack(); + Advance(opCode); + } break; + case RegExpOpCode::OP_RANGE32: + if (!HandleOpRange32(byteCode)) { + return false; + } + break; + case RegExpOpCode::OP_RANGE: + if (!HandleOpRange(byteCode)) { + return false; + } + break; + case RegExpOpCode::OP_BACKREFERENCE: + case RegExpOpCode::OP_BACKWARD_BACKREFERENCE: + if (!HandleOpBackReference(byteCode, opCode)) { + return false; + } + break; + default: + UNREACHABLE(); + } + } + // for loop match + return true; +} + +void RegExpExecutor::DumpResult(std::ostream &out) const +{ + out << "captures:" << std::endl; + for (uint32_t i = 0; i < nCapture_; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + CaptureState *captureState = &captureResultList_[i]; + int32_t len = captureState->captureEnd - captureState->captureStart; + if ((captureState->captureStart != nullptr && captureState->captureEnd != nullptr) && (len >= 0)) { + out << i << ":\t" << CString(reinterpret_cast(captureState->captureStart), len) + << std::endl; + } else { + out << i << ":\t" + << "undefined" << std::endl; + } + } +} + +MatchResult RegExpExecutor::GetResult(const JSThread *thread, bool isSuccess) const +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + MatchResult result; + std::vector>> captures; + result.isSuccess_ = isSuccess; + if (isSuccess) { + for (uint32_t i = 0; i < nCapture_; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + CaptureState *captureState = &captureResultList_[i]; + if (i == 0) { + result.index_ = captureState->captureStart - input_; + if (isWideChar_) { + result.index_ /= WIDE_CHAR_SIZE; + } + } + int32_t len = captureState->captureEnd - captureState->captureStart; + std::pair> pair; + if ((captureState->captureStart != nullptr && captureState->captureEnd != nullptr) && (len >= 0)) { + pair.first = false; + if (isWideChar_) { + // create utf-16 string + pair.second = + factory->NewFromUtf16(reinterpret_cast(captureState->captureStart), len / 2); + } else { + // create utf-8 string + CVector buffer(len + 1); + uint8_t *dest = buffer.data(); + if (memcpy_s(dest, len + 1, reinterpret_cast(captureState->captureStart), len) != + EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + dest[len] = '\0'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + pair.second = factory->NewFromUtf8(reinterpret_cast(buffer.data()), len); + } + } else { + // undefined + pair.first = true; + } + captures.emplace_back(pair); + } + result.captures_ = captures; + result.endIndex_ = currentPtr_ - input_; + if (isWideChar_) { + result.endIndex_ /= WIDE_CHAR_SIZE; + } + } + return result; +} + +void RegExpExecutor::PushRegExpState(StateType type, uint32_t pc) +{ + ReAllocStack(stateStackLen_ + 1); + auto state = reinterpret_cast( + stateStack_ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + stateStackLen_ * stateSize_); + state->type_ = type; + state->currentPc_ = pc; + state->currentStack_ = currentStack_; + state->currentPtr_ = GetCurrentPtr(); + size_t listSize = sizeof(CaptureState) * nCapture_; + if (memcpy_s(state->captureResultList_, listSize, GetCaptureResultList(), listSize) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + uint8_t *stackStart = + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + reinterpret_cast(state->captureResultList_) + sizeof(CaptureState) * nCapture_; + if (stack_ != nullptr) { + size_t stackSize = sizeof(uintptr_t) * nStack_; + if (memcpy_s(stackStart, stackSize, stack_, stackSize) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } + stateStackLen_++; +} + +RegExpState *RegExpExecutor::PopRegExpState(bool copyCaptrue) +{ + if (stateStackLen_ != 0) { + auto state = PeekRegExpState(); + size_t listSize = sizeof(CaptureState) * nCapture_; + if (copyCaptrue) { + if (memcpy_s(GetCaptureResultList(), listSize, state->captureResultList_, listSize) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } + SetCurrentPtr(state->currentPtr_); + SetCurrentPC(state->currentPc_); + currentStack_ = state->currentStack_; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint8_t *stackStart = reinterpret_cast(state->captureResultList_) + listSize; + if (stack_ != nullptr) { + size_t stackSize = sizeof(uintptr_t) * nStack_; + if (memcpy_s(stack_, stackSize, stackStart, stackSize) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } + stateStackLen_--; + return state; + } + return nullptr; +} + +void RegExpExecutor::ReAllocStack(uint32_t stackLen) +{ + if (stackLen > stateStackSize_) { + uint32_t newStackSize = std::max(stateStackSize_ * 2, MIN_STACK_SIZE); // 2: double the size + uint32_t stackByteSize = newStackSize * stateSize_; + auto newStack = chunk_->NewArray(stackByteSize); + if (memset_s(newStack, stackByteSize, 0, stackByteSize) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + if (stateStack_ != nullptr) { + size_t stackSize = stateStackSize_ * stateSize_; + if (memcpy_s(newStack, stackSize, stateStack_, stackSize) != EOK) { + return; + } + } + stateStack_ = newStack; + stateStackSize_ = newStackSize; + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/regexp/regexp_executor.h b/ecmascript/regexp/regexp_executor.h new file mode 100644 index 0000000000000000000000000000000000000000..5eca696139092774affa2634d9c8617a5c992ec3 --- /dev/null +++ b/ecmascript/regexp/regexp_executor.h @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_REGEXP_EXECUTOR_H +#define PANDA_RUNTIME_ECMASCRIPT_REGEXP_EXECUTOR_H + +#include "ecmascript/regexp/regexp_parser.h" +#include "ecmascript/mem/chunk.h" + +namespace panda::ecmascript { +class RegExpExecutor { +public: + struct CaptureState { + const uint8_t *captureStart; + const uint8_t *captureEnd; + }; + + enum StateType : uint8_t { + STATE_SPLIT = 0, + STATE_MATCH_AHEAD, + STATE_NEGATIVE_MATCH_AHEAD, + }; + + struct RegExpState { + StateType type_ = STATE_SPLIT; + uint32_t currentPc_ = 0; + uint32_t currentStack_ = 0; + const uint8_t *currentPtr_ = nullptr; + __extension__ CaptureState *captureResultList_[0]; // NOLINT(modernize-avoid-c-arrays) + }; + + struct MatchResult { + uint32_t endIndex_ = 0; + uint32_t index_ = 0; + // first value is true if result is undefined + std::vector>> captures_; + bool isSuccess_ = false; + }; + + explicit RegExpExecutor(Chunk *chunk) : chunk_(chunk) + { + ASSERT(chunk_ != nullptr); + }; + + ~RegExpExecutor() = default; + + NO_COPY_SEMANTIC(RegExpExecutor); + NO_MOVE_SEMANTIC(RegExpExecutor); + + bool Execute(const uint8_t *input, uint32_t lastIndex, uint32_t length, uint8_t *buf, bool isWideChar = false); + + bool ExecuteInternal(const DynChunk &byteCode, uint32_t pcEnd); + bool HandleFirstSplit(); + bool HandleOpAll(uint8_t opCode); + bool HandleOpChar(const DynChunk &byteCode, uint8_t opCode); + bool HandleOpWordBoundary(uint8_t opCode); + bool HandleOpLineStart(uint8_t opCode); + bool HandleOpLineEnd(uint8_t opCode); + void HandleOpSaveStart(const DynChunk &byteCode, uint8_t opCode); + void HandleOpSaveEnd(const DynChunk &byteCode, uint8_t opCode); + void HandleOpSaveReset(const DynChunk &byteCode, uint8_t opCode); + void HandleOpMatch(const DynChunk &byteCode, uint8_t opCode); + void HandleOpSplitFirst(const DynChunk &byteCode, uint8_t opCode); + bool HandleOpPrev(uint8_t opCode); + void HandleOpLoop(const DynChunk &byteCode, uint8_t opCode); + bool HandleOpRange32(const DynChunk &byteCode); + bool HandleOpRange(const DynChunk &byteCode); + bool HandleOpBackReference(const DynChunk &byteCode, uint8_t opCode); + + inline void Advance(uint8_t opCode, uint32_t offset = 0) + { + currentPc_ += offset + RegExpOpCode::GetRegExpOpCode(opCode)->GetSize(); + } + + inline void AdvanceOffset(uint32_t offset) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + currentPc_ += offset; + } + + inline uint32_t GetCurrentChar() + { + return GetChar(¤tPtr_, inputEnd_); + } + + inline void AdvanceCurrentPtr() + { + AdvancePtr(¤tPtr_, inputEnd_); + } + + uint32_t GetChar(const uint8_t **pp, const uint8_t *end) const + { + uint32_t c; + const uint8_t *cptr = *pp; + if (!isWideChar_) { + c = *cptr; + *pp += 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else { + uint16_t c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + c = c1; + cptr += WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (U16_IS_LEAD(c) && IsUtf16() && cptr < end) { + c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + if (U16_IS_TRAIL(c1)) { + c = U16_GET_SUPPLEMENTARY(c, c1); // NOLINTNEXTLINE(hicpp-signed-bitwise) + cptr += WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } + *pp = cptr; + } + return c; + } + + uint32_t PeekChar(const uint8_t *p, const uint8_t *end) const + { + uint32_t c; + const uint8_t *cptr = p; + if (!isWideChar_) { + c = *cptr; + } else { + uint16_t c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + c = c1; + cptr += WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (U16_IS_LEAD(c) && IsUtf16() && cptr < end) { + c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + if (U16_IS_TRAIL(c1)) { + c = U16_GET_SUPPLEMENTARY(c, c1); // NOLINTNEXTLINE(hicpp-signed-bitwise) + cptr += WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } + } + return c; + } + + void AdvancePtr(const uint8_t **pp, const uint8_t *end) const + { + const uint8_t *cptr = *pp; + if (!isWideChar_) { + *pp += 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else { + uint16_t c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + cptr += WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (U16_IS_LEAD(c1) && IsUtf16() && cptr < end) { + c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + if (U16_IS_TRAIL(c1)) { + cptr += WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } + *pp = cptr; + } + } + + uint32_t PeekPrevChar(const uint8_t *p, const uint8_t *start) const + { + uint32_t c; + const uint8_t *cptr = p; + if (!isWideChar_) { + c = *(cptr - 1); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + cptr -= 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else { + cptr -= WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint16_t c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + c = c1; + if (U16_IS_TRAIL(c) && IsUtf16() && cptr > start) { + c1 = ((uint16_t *)cptr)[-1]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (U16_IS_LEAD(c1)) { + c = U16_GET_SUPPLEMENTARY(c1, c); // NOLINTNEXTLINE(hicpp-signed-bitwise) + cptr -= WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } + } + return c; + } + + uint32_t GetPrevChar(const uint8_t **pp, const uint8_t *start) const + { + uint32_t c; + const uint8_t *cptr = *pp; + if (!isWideChar_) { + c = *(cptr - 1); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + cptr -= 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *pp = cptr; + } else { + cptr -= WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint16_t c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + c = c1; + if (U16_IS_TRAIL(c) && IsUtf16() && cptr > start) { + c1 = ((uint16_t *)cptr)[-1]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (U16_IS_LEAD(c1)) { + c = U16_GET_SUPPLEMENTARY(c1, c); // NOLINTNEXTLINE(hicpp-signed-bitwise) + cptr -= WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } + *pp = cptr; + } + return c; + } + + void PrevPtr(const uint8_t **pp, const uint8_t *start) const + { + const uint8_t *cptr = *pp; + if (!isWideChar_) { + cptr -= 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *pp = cptr; + } else { + cptr -= WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint16_t c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + if (U16_IS_TRAIL(c1) && IsUtf16() && cptr > start) { + c1 = ((uint16_t *)cptr)[-1]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (U16_IS_LEAD(c1)) { + cptr -= WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } + *pp = cptr; + } + } + + bool MatchFailed(bool isMatched = false); + + void SetCurrentPC(uint32_t pc) + { + currentPc_ = pc; + } + + void SetCurrentPtr(const uint8_t *ptr) + { + currentPtr_ = ptr; + } + + bool IsEOF() const + { + return currentPtr_ >= inputEnd_; + } + + uint32_t GetCurrentPC() const + { + return currentPc_; + } + + void PushStack(uintptr_t val) + { + ASSERT(currentStack_ < nStack_); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + stack_[currentStack_++] = val; + } + + void SetStackValue(uintptr_t val) const + { + ASSERT(currentStack_ >= 1); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + stack_[currentStack_ - 1] = val; + } + + uintptr_t PopStack() + { + ASSERT(currentStack_ >= 1); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return stack_[--currentStack_]; + } + + uintptr_t PeekStack() const + { + ASSERT(currentStack_ >= 1); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return stack_[currentStack_ - 1]; + } + + const uint8_t *GetCurrentPtr() const + { + return currentPtr_; + } + + CaptureState *GetCaptureResultList() const + { + return captureResultList_; + } + + void DumpResult(std::ostream &out) const; + + MatchResult GetResult(const JSThread *thread, bool isSuccess) const; + + void PushRegExpState(StateType type, uint32_t pc); + + RegExpState *PopRegExpState(bool copyCaptrue = true); + + void DropRegExpState() + { + stateStackLen_--; + } + + RegExpState *PeekRegExpState() const + { + ASSERT(stateStackLen_ >= 1); + return reinterpret_cast( + stateStack_ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + (stateStackLen_ - 1) * stateSize_); + } + + void ReAllocStack(uint32_t stackLen); + + inline bool IsWordChar(uint8_t value) const + { + return ((value >= '0' && value <= '9') || (value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || + (value == '_')); + } + + inline bool IsTerminator(uint32_t value) const + { + // NOLINTNEXTLINE(readability-magic-numbers) + return (value == '\n' || value == '\r' || value == 0x2028 || value == 0x2029); + } + + inline bool IsIgnoreCase() const + { + return (flags_ & RegExpParser::FLAG_IGNORECASE) != 0; + } + + inline bool IsUtf16() const + { + return (flags_ & RegExpParser::FLAG_UTF16) != 0; + } + +private: + static constexpr size_t CHAR_SIZE = 1; + static constexpr size_t WIDE_CHAR_SIZE = 2; + static constexpr size_t SAVE_RESET_START = 1; + static constexpr size_t SAVE_RESET_END = 2; + static constexpr size_t LOOP_MIN_OFFSET = 5; + static constexpr size_t LOOP_MAX_OFFSET = 9; + static constexpr size_t LOOP_PC_OFFSET = 1; + static constexpr size_t RANGE32_HEAD_OFFSET = 3; + static constexpr size_t RANGE32_MAX_HALF_OFFSET = 4; + static constexpr size_t RANGE32_MAX_OFFSET = 8; + static constexpr size_t RANGE32_OFFSET = 2; + static constexpr uint32_t STACK_MULTIPLIER = 2; + static constexpr uint32_t MIN_STACK_SIZE = 8; + uint8_t *input_ = nullptr; + uint8_t *inputEnd_ = nullptr; + bool isWideChar_ = false; + + uint32_t currentPc_ = 0; + const uint8_t *currentPtr_ = nullptr; + CaptureState *captureResultList_ = nullptr; + uintptr_t *stack_ = nullptr; + uint32_t currentStack_ = 0; + + uint32_t nCapture_ = 0; + uint32_t nStack_ = 0; + + uint32_t flags_ = 0; + uint32_t stateStackLen_ = 0; + uint32_t stateStackSize_ = 0; + uint32_t stateSize_ = 0; + uint8_t *stateStack_ = nullptr; + Chunk *chunk_ = nullptr; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_REGEXP_EXECUTOR_H diff --git a/ecmascript/regexp/regexp_opcode.cpp b/ecmascript/regexp/regexp_opcode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8aad2de82f7d53dd3f816a2ba366b412e2c49ae --- /dev/null +++ b/ecmascript/regexp/regexp_opcode.cpp @@ -0,0 +1,666 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/regexp/regexp_opcode.h" + +#include "ecmascript/regexp/regexp_executor.h" + +namespace panda::ecmascript { +using CaptureState = RegExpExecutor::CaptureState; + +static SaveStartOpCode g_saveStartOpcode = SaveStartOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static SaveEndOpCode g_saveEndOpcode = SaveEndOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static CharOpCode g_charOpcode = CharOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static GotoOpCode g_gotoOpcode = GotoOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static SplitNextOpCode g_splitNextOpcode = SplitNextOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static SplitFirstOpCode g_splitFirstOpcode = + SplitFirstOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static MatchOpCode g_matchOpcode = MatchOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static LoopOpCode g_loopOpcode = LoopOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static LoopGreedyOpCode g_loopGreedyOpcode = + LoopGreedyOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static PushCharOpCode g_pushCharOpcode = PushCharOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static CheckCharOpCode g_checkCharOpcode = CheckCharOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static PushOpCode g_pushOpcode = PushOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static PopOpCode g_popOpcode = PopOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static SaveResetOpCode g_saveResetOpcode = SaveResetOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static LineStartOpCode g_lineStartOpcode = LineStartOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static LineEndOpCode g_lineEndOpcode = LineEndOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static WordBoundaryOpCode g_wordBoundaryOpcode = + WordBoundaryOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static NotWordBoundaryOpCode g_notWordBoundaryOpcode = + NotWordBoundaryOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static AllOpCode g_allOpcode = AllOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static DotsOpCode g_dotsOpcode = DotsOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static MatchAheadOpCode g_matchAheadOpcode = + MatchAheadOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static NegativeMatchAheadOpCode g_negativeMatchAheadOpcode = + NegativeMatchAheadOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static MatchEndOpCode g_matchEndOpcode = MatchEndOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static PrevOpCode g_prevOpcode = PrevOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static RangeOpCode g_rangeOpcode = RangeOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static BackReferenceOpCode g_backreferenceOpcode = + BackReferenceOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static BackwardBackReferenceOpCode g_backwardBackreferenceOpcode = + BackwardBackReferenceOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static Char32OpCode g_char32Opcode = Char32OpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static Range32OpCode g_range32Opcode = Range32OpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static std::vector g_intrinsicSet = { + &g_saveStartOpcode, + &g_saveEndOpcode, + &g_charOpcode, + &g_gotoOpcode, + &g_splitFirstOpcode, + &g_splitNextOpcode, + &g_matchAheadOpcode, + &g_negativeMatchAheadOpcode, + &g_matchOpcode, + &g_loopOpcode, + &g_loopGreedyOpcode, + &g_pushCharOpcode, + &g_checkCharOpcode, + &g_pushOpcode, + &g_popOpcode, + &g_saveResetOpcode, + &g_lineStartOpcode, + &g_lineEndOpcode, + &g_wordBoundaryOpcode, + &g_notWordBoundaryOpcode, + &g_allOpcode, + &g_dotsOpcode, + &g_matchEndOpcode, + &g_prevOpcode, + &g_rangeOpcode, + &g_backreferenceOpcode, + &g_backwardBackreferenceOpcode, + &g_char32Opcode, + &g_range32Opcode, +}; + +RegExpOpCode::RegExpOpCode(uint8_t opCode, int size) : opCode_(opCode), size_(size) {} + +/* static */ +RegExpOpCode *RegExpOpCode::GetRegExpOpCode(const DynChunk &buf, int pc) +{ + uint8_t opCode = buf.GetU8(pc); + ASSERT_PRINT(opCode <= g_intrinsicSet.size(), "invalid op code"); + return g_intrinsicSet.at(opCode); +} + +/* static */ +RegExpOpCode *RegExpOpCode::GetRegExpOpCode(uint8_t opCode) +{ + ASSERT_PRINT(opCode <= g_intrinsicSet.size(), "invalid op code"); + return g_intrinsicSet.at(opCode); +} + +/* static */ +void RegExpOpCode::DumpRegExpOpCode(std::ostream &out, const DynChunk &buf) +{ + out << "OpCode:\t" << std::endl; + uint32_t pc = RegExpParser::OP_START_OFFSET; + do { + RegExpOpCode *byteCode = GetRegExpOpCode(buf, pc); + pc = byteCode->DumpOpCode(out, buf, pc); + } while (pc < buf.size_); +} + +uint32_t SaveStartOpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + auto capture = static_cast(para & 0xffU); // NOLINTNEXTLINE(readability-magic-numbers) + buf->EmitChar(GetOpCode()); + buf->EmitChar(capture); + return GetDynChunkfSize(*buf); +} + +uint32_t SaveStartOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "save_start\t" << buf.GetU8(offset + 1) << std::endl; + return offset + GetSize(); +} + +uint32_t SaveEndOpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + auto capture = static_cast(para & 0xffU); // NOLINTNEXTLINE(readability-magic-numbers) + buf->EmitChar(GetOpCode()); + buf->EmitChar(capture); + return GetDynChunkfSize(*buf); +} + +uint32_t SaveEndOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "save_end\t" << buf.GetU8(offset + 1) << std::endl; + return offset + GetSize(); +} + +uint32_t CharOpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + auto paraChar = static_cast(para & 0xffffU); // NOLINTNEXTLINE(readability-magic-numbers) + buf->EmitChar(GetOpCode()); + buf->EmitU16(paraChar); + return GetDynChunkfSize(*buf); +} + +uint32_t CharOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "char\t" << static_cast(buf.GetU16(offset + 1)) << std::endl; + return offset + GetSize(); +} + +uint32_t Char32OpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + buf->EmitU32(para); + return GetDynChunkfSize(*buf); +} + +uint32_t Char32OpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "char32\t" << static_cast(buf.GetU32(offset + 1)) << std::endl; + return offset + GetSize(); +} + +uint32_t GotoOpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + buf->EmitU32(para); + return GetDynChunkfSize(*buf); +} + +void GotoOpCode::UpdateOpPara(DynChunk *buf, uint32_t offset, uint32_t para) const +{ + buf->PutU32(offset + 1, para); +} + +uint32_t GotoOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "goto\t" << buf.GetU32(offset + 1) + offset + GetSize() << std::endl; + return offset + GetSize(); +} + +uint32_t SplitNextOpCode::InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const +{ + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + buf->PutU32(offset + 1, para); + return GetDynChunkfSize(*buf); +} + +uint32_t SplitNextOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "split_next\t" << buf.GetU32(offset + 1) + offset + GetSize() << std::endl; + return offset + GetSize(); +} + +uint32_t SplitFirstOpCode::InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const +{ + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + buf->PutU32(offset + 1, para); + return GetDynChunkfSize(*buf); +} + +uint32_t SplitFirstOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "split_first\t" << buf.GetU32(offset + 1) + offset + GetSize() << std::endl; + return offset + GetSize(); +} + +uint32_t LoopOpCode::EmitOpCode(DynChunk *buf, uint32_t start, uint32_t min, uint32_t max) const +{ + buf->EmitChar(GetOpCode()); + buf->EmitU32(start); + buf->EmitU32(min); + buf->EmitU32(max); + return GetDynChunkfSize(*buf); +} + +uint32_t LoopOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "loop\t" << buf.GetU32(offset + 1) + offset + GetSize() << "\t" + << buf.GetU32(offset + RegExpOpCode::OP_SIZE_FIVE) << "\t" << buf.GetU32(offset + RegExpOpCode::OP_SIZE_NINE) + << std::endl; + return offset + GetSize(); +} + +uint32_t LoopGreedyOpCode::EmitOpCode(DynChunk *buf, uint32_t start, uint32_t min, uint32_t max) const +{ + buf->EmitChar(GetOpCode()); + buf->EmitU32(start); + buf->EmitU32(min); + buf->EmitU32(max); + return GetDynChunkfSize(*buf); +} + +uint32_t LoopGreedyOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "greedy_loop\t" << buf.GetU32(offset + 1) + offset + GetSize() << "\t" + << buf.GetU32(offset + RegExpOpCode::OP_SIZE_FIVE) << "\t" << buf.GetU32(offset + RegExpOpCode::OP_SIZE_NINE) + << std::endl; + return offset + GetSize(); +} + +uint32_t PushCharOpCode::InsertOpCode(DynChunk *buf, uint32_t offset) const +{ + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t PushCharOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "push_char" << std::endl; + return offset + GetSize(); +} + +uint32_t PushOpCode::InsertOpCode(DynChunk *buf, uint32_t offset) const +{ + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t PushOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "push" << std::endl; + return offset + GetSize(); +} + +uint32_t PopOpCode::EmitOpCode(DynChunk *buf) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t PopOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "pop" << std::endl; + return offset + GetSize(); +} + +uint32_t CheckCharOpCode::EmitOpCode(DynChunk *buf, uint32_t offset) const +{ + buf->EmitChar(GetOpCode()); + buf->EmitU32(offset); + return GetDynChunkfSize(*buf); +} + +uint32_t CheckCharOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "check_char\t" << buf.GetU32(offset + 1) + offset + GetSize() << std::endl; + return offset + GetSize(); +} + +uint32_t SaveResetOpCode::InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t start, uint32_t end) const +{ + auto captureStart = static_cast(start & 0xffU); // NOLINTNEXTLINE(readability-magic-numbers) + auto captureEnd = static_cast(end & 0xffU); // NOLINTNEXTLINE(readability-magic-numbers) + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + buf->PutU8(offset + RegExpOpCode::OP_SIZE_ONE, captureStart); + buf->PutU8(offset + RegExpOpCode::OP_SIZE_TWO, captureEnd); + return GetDynChunkfSize(*buf); +} + +uint32_t SaveResetOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "save_reset\t" << buf.GetU8(offset + RegExpOpCode::OP_SIZE_ONE) << "\t" + << buf.GetU8(offset + RegExpOpCode::OP_SIZE_TWO) << std::endl; + return offset + GetSize(); +} + +uint32_t MatchOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t MatchOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "match" << std::endl; + return offset + GetSize(); +} + +uint32_t MatchEndOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t MatchEndOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "match_end" << std::endl; + return offset + GetSize(); +} + +uint32_t LineStartOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t LineStartOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "line_start" << std::endl; + return offset + GetSize(); +} + +uint32_t LineEndOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t LineEndOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "line_end" << std::endl; + return offset + GetSize(); +} + +uint32_t WordBoundaryOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t WordBoundaryOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "word_boundary" << std::endl; + return offset + GetSize(); +} + +uint32_t NotWordBoundaryOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t NotWordBoundaryOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, + uint32_t offset) const +{ + out << offset << ":\t" + << "not_word_boundary" << std::endl; + return offset + GetSize(); +} + +uint32_t AllOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t AllOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "all" << std::endl; + return offset + GetSize(); +} + +uint32_t DotsOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t DotsOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "dots" << std::endl; + return offset + GetSize(); +} + +uint32_t MatchAheadOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "match_ahead\t" << buf.GetU32(offset + 1) + offset + GetSize() << std::endl; + return offset + GetSize(); +} + +uint32_t RangeOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "range\t"; + int size = buf.GetU16(offset + 1); + for (int i = 0; i < size; i++) { + out << buf.GetU16(offset + RegExpOpCode::OP_SIZE_THREE + (i * RegExpOpCode::OP_SIZE_FOUR)) << "\t" + << buf.GetU16(offset + RegExpOpCode::OP_SIZE_THREE + + (i * RegExpOpCode::OP_SIZE_FOUR + RegExpOpCode::OP_SIZE_TWO)) + << "\t"; + } + out << std::endl; + return offset + size * RegExpOpCode::OP_SIZE_FOUR + RegExpOpCode::OP_SIZE_THREE; +} + +uint32_t RangeOpCode::InsertOpCode(DynChunk *buf, const RangeSet &rangeSet) const +{ + buf->EmitChar(GetOpCode()); + size_t size = rangeSet.rangeSet_.size(); + buf->EmitU16(size); + for (auto range : rangeSet.rangeSet_) { + buf->EmitU16(range.first); + buf->EmitU16(range.second); + } + return GetDynChunkfSize(*buf); +} + +uint32_t Range32OpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "range32\t"; + int size = buf.GetU16(offset + 1); + for (int i = 0; i < size; i++) { + out << buf.GetU32(offset + RegExpOpCode::OP_SIZE_THREE + (i * RegExpOpCode::OP_SIZE_EIGHT)) << "\t" + << buf.GetU32(offset + RegExpOpCode::OP_SIZE_THREE + + (i * RegExpOpCode::OP_SIZE_EIGHT + RegExpOpCode::OP_SIZE_FOUR)) + << "\t"; + } + out << std::endl; + return offset + size * +RegExpOpCode::OP_SIZE_EIGHT + RegExpOpCode::OP_SIZE_THREE; +} + +uint32_t Range32OpCode::InsertOpCode(DynChunk *buf, const RangeSet &rangeSet) const +{ + buf->EmitChar(GetOpCode()); + size_t size = rangeSet.rangeSet_.size(); + buf->EmitU16(size); + for (auto range : rangeSet.rangeSet_) { + buf->EmitU32(range.first); + buf->EmitU32(range.second); + } + return GetDynChunkfSize(*buf); +} + +uint32_t MatchAheadOpCode::InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const +{ + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + buf->PutU32(offset + 1, para); + return GetDynChunkfSize(*buf); +} + +uint32_t NegativeMatchAheadOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "negative_match_ahead\t" << buf.GetU32(offset + 1) + offset + GetSize() << std::endl; + return offset + GetSize(); +} + +uint32_t NegativeMatchAheadOpCode::InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const +{ + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + buf->PutU32(offset + 1, para); + return GetDynChunkfSize(*buf); +} + +uint32_t PrevOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t PrevOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "prev" << std::endl; + return offset + GetSize(); +} + +uint32_t BackReferenceOpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + auto capture = static_cast(para & 0xffU); // NOLINTNEXTLINE(readability-magic-numbers) + buf->EmitChar(GetOpCode()); + buf->EmitChar(capture); + return GetDynChunkfSize(*buf); +} + +uint32_t BackReferenceOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "backreference\t" << buf.GetU8(offset + 1) << std::endl; + return offset + GetSize(); +} + +uint32_t BackwardBackReferenceOpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + auto capture = static_cast(para & 0xffU); // NOLINTNEXTLINE(readability-magic-numbers) + buf->EmitChar(GetOpCode()); + buf->EmitChar(capture); + return GetDynChunkfSize(*buf); +} + +uint32_t BackwardBackReferenceOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "backward_backreference\t" << buf.GetU8(offset + 1) << std::endl; + return offset + GetSize(); +} + +void RangeSet::Insert(uint32_t start, uint32_t end) +{ + if (start > end) { + return; + } + std::pair pairElement = std::make_pair(start, end); + if (rangeSet_.empty()) { + rangeSet_.emplace_back(pairElement); + } else { + for (auto iter = rangeSet_.begin(); iter != rangeSet_.end(); iter++) { + if (IsIntersect(start, end, iter->first, iter->second) || + IsAdjacent(start, end, iter->first, iter->second)) { + iter->first = std::min(iter->first, start); + iter->second = std::max(iter->second, end); + return; + } + if (iter->first > end) { + rangeSet_.insert(iter, pairElement); + return; + } + } + rangeSet_.emplace_back(pairElement); + } +} + +void RangeSet::Insert(const RangeSet &s1) +{ + if (s1.rangeSet_.empty()) { + return; + } + if (rangeSet_.empty()) { + rangeSet_ = s1.rangeSet_; + } else { + for (auto range : s1.rangeSet_) { + Insert(range.first, range.second); + } + Compress(); + } +} + +void RangeSet::Invert(bool isUtf16) +{ + uint32_t maxValue = isUtf16 ? UINT32_MAX : UINT16_MAX; + if (rangeSet_.empty()) { + rangeSet_.emplace_back(std::make_pair(0, maxValue)); + return; + } + + auto iter = rangeSet_.begin(); + auto iter2 = rangeSet_.begin(); + if (iter->first == 0 && iter->second == maxValue) { + rangeSet_.clear(); + return; + } + iter2++; + + uint32_t first = iter->first; + + for (iter = rangeSet_.begin(); iter != rangeSet_.end(); iter++) { + if (iter->second == maxValue) { + rangeSet_.erase(iter); + break; + } + iter->first = iter->second + 1; + if (iter2 != rangeSet_.end()) { + iter->second = iter2->first - 1; + iter2++; + } else { + iter->second = maxValue; + } + } + if (first > 0) { + std::pair pair1 = std::make_pair(0, first - 1); + rangeSet_.push_front(pair1); + } + Compress(); +} + +void RangeSet::Compress() +{ + auto iter = rangeSet_.begin(); + auto iter2 = rangeSet_.begin(); + iter2++; + while (iter2 != rangeSet_.end()) { + if (IsIntersect(iter->first, iter->second, iter2->first, iter2->second) || + IsAdjacent(iter->first, iter->second, iter2->first, iter2->second)) { + iter->first = std::min(iter->first, iter2->first); + iter->second = std::max(iter->second, iter2->second); + iter2 = rangeSet_.erase(iter2); + } else { + iter++; + iter2++; + } + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/regexp/regexp_opcode.h b/ecmascript/regexp/regexp_opcode.h new file mode 100644 index 0000000000000000000000000000000000000000..efe4cf9a8dea5af4abf28bf854859418278145d2 --- /dev/null +++ b/ecmascript/regexp/regexp_opcode.h @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMA_REGEXP_OPCODE_H +#define PANDA_RUNTIME_ECMA_REGEXP_OPCODE_H + +#include + +#include "ecmascript/regexp/dyn_chunk.h" + +namespace panda { +namespace ecmascript { +class RegExpOpCode { +public: + enum : uint8_t { + OP_SAVE_START = 0U, + OP_SAVE_END, + OP_CHAR, + OP_GOTO, + OP_SPLIT_FIRST, + OP_SPLIT_NEXT, + OP_MATCH_AHEAD, + OP_NEGATIVE_MATCH_AHEAD, + OP_MATCH, + OP_LOOP, + OP_LOOP_GREEDY, + OP_PUSH_CHAR, + OP_CHECK_CHAR, + OP_PUSH, + OP_POP, + OP_SAVE_RESET, + OP_LINE_START, + OP_LINE_END, + OP_WORD_BOUNDARY, + OP_NOT_WORD_BOUNDARY, + OP_ALL, + OP_DOTS, + OP_MATCH_END, + OP_PREV, + OP_RANGE, + OP_BACKREFERENCE, + OP_BACKWARD_BACKREFERENCE, + OP_CHAR32, + OP_RANGE32, + OP_INVALID, + }; + + static constexpr size_t OP_SIZE_ONE = 1; + static constexpr size_t OP_SIZE_TWO = 2; + static constexpr size_t OP_SIZE_THREE = 3; + static constexpr size_t OP_SIZE_FOUR = 4; + static constexpr size_t OP_SIZE_FIVE = 5; + static constexpr size_t OP_SIZE_EIGHT = 8; + static constexpr size_t OP_SIZE_NINE = 9; + static constexpr size_t OP_SIZE_THIRTEEN = 13; + + RegExpOpCode(uint8_t opCode, int size); + NO_COPY_SEMANTIC(RegExpOpCode); + NO_MOVE_SEMANTIC(RegExpOpCode); + + virtual ~RegExpOpCode() = default; + static RegExpOpCode *GetRegExpOpCode(const DynChunk &buf, int pcOffset); + static RegExpOpCode *GetRegExpOpCode(uint8_t opCode); + static void DumpRegExpOpCode(std::ostream &out, const DynChunk &buf); + inline int GetSize() const + { + return size_; + } + inline uint8_t GetOpCode() const + { + return opCode_; + } + inline int GetDynChunkfSize(const DynChunk &buf) const + { + return buf.size_; + } + virtual uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const = 0; + +private: + uint8_t opCode_{0}; + uint8_t size_{0}; +}; + +class SaveStartOpCode : public RegExpOpCode { +public: + SaveStartOpCode() : RegExpOpCode(OP_SAVE_START, RegExpOpCode::OP_SIZE_TWO) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~SaveStartOpCode() override = default; + NO_COPY_SEMANTIC(SaveStartOpCode); + NO_MOVE_SEMANTIC(SaveStartOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class SaveEndOpCode : public RegExpOpCode { +public: + SaveEndOpCode() : RegExpOpCode(OP_SAVE_END, RegExpOpCode::OP_SIZE_TWO) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~SaveEndOpCode() override = default; + NO_COPY_SEMANTIC(SaveEndOpCode); + NO_MOVE_SEMANTIC(SaveEndOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class CharOpCode : public RegExpOpCode { +public: + CharOpCode() : RegExpOpCode(OP_CHAR, RegExpOpCode::OP_SIZE_THREE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~CharOpCode() override = default; + NO_COPY_SEMANTIC(CharOpCode); + NO_MOVE_SEMANTIC(CharOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class GotoOpCode : public RegExpOpCode { +public: + GotoOpCode() : RegExpOpCode(OP_GOTO, RegExpOpCode::OP_SIZE_FIVE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + void UpdateOpPara(DynChunk *buf, uint32_t offset, uint32_t para) const; + ~GotoOpCode() override = default; + NO_COPY_SEMANTIC(GotoOpCode); + NO_MOVE_SEMANTIC(GotoOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class SplitNextOpCode : public RegExpOpCode { +public: + SplitNextOpCode() : RegExpOpCode(OP_SPLIT_NEXT, RegExpOpCode::OP_SIZE_FIVE) {} + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const; + ~SplitNextOpCode() override = default; + NO_COPY_SEMANTIC(SplitNextOpCode); + NO_MOVE_SEMANTIC(SplitNextOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class SplitFirstOpCode : public RegExpOpCode { +public: + SplitFirstOpCode() : RegExpOpCode(OP_SPLIT_FIRST, RegExpOpCode::OP_SIZE_FIVE) {} + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const; + ~SplitFirstOpCode() override = default; + NO_COPY_SEMANTIC(SplitFirstOpCode); + NO_MOVE_SEMANTIC(SplitFirstOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class PushOpCode : public RegExpOpCode { +public: + PushOpCode() : RegExpOpCode(OP_PUSH, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset) const; + ~PushOpCode() override = default; + NO_COPY_SEMANTIC(PushOpCode); + NO_MOVE_SEMANTIC(PushOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class PopOpCode : public RegExpOpCode { +public: + PopOpCode() : RegExpOpCode(OP_POP, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf) const; + ~PopOpCode() override = default; + NO_COPY_SEMANTIC(PopOpCode); + NO_MOVE_SEMANTIC(PopOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class PushCharOpCode : public RegExpOpCode { +public: + PushCharOpCode() : RegExpOpCode(OP_PUSH_CHAR, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset) const; + ~PushCharOpCode() override = default; + NO_COPY_SEMANTIC(PushCharOpCode); + NO_MOVE_SEMANTIC(PushCharOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class CheckCharOpCode : public RegExpOpCode { +public: + CheckCharOpCode() : RegExpOpCode(OP_CHECK_CHAR, RegExpOpCode::OP_SIZE_FIVE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t offset) const; + ~CheckCharOpCode() override = default; + NO_COPY_SEMANTIC(CheckCharOpCode); + NO_MOVE_SEMANTIC(CheckCharOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class LoopOpCode : public RegExpOpCode { +public: + LoopOpCode() : RegExpOpCode(OP_LOOP, RegExpOpCode::OP_SIZE_THIRTEEN) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t start, uint32_t min, uint32_t max) const; + ~LoopOpCode() override = default; + NO_COPY_SEMANTIC(LoopOpCode); + NO_MOVE_SEMANTIC(LoopOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class LoopGreedyOpCode : public RegExpOpCode { +public: + LoopGreedyOpCode() : RegExpOpCode(OP_LOOP_GREEDY, RegExpOpCode::OP_SIZE_THIRTEEN) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t start, uint32_t min, uint32_t max) const; + ~LoopGreedyOpCode() override = default; + NO_COPY_SEMANTIC(LoopGreedyOpCode); + NO_MOVE_SEMANTIC(LoopGreedyOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class SaveResetOpCode : public RegExpOpCode { +public: + SaveResetOpCode() : RegExpOpCode(OP_SAVE_RESET, RegExpOpCode::OP_SIZE_THREE) {} + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t start, uint32_t end) const; + ~SaveResetOpCode() override = default; + NO_COPY_SEMANTIC(SaveResetOpCode); + NO_MOVE_SEMANTIC(SaveResetOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class MatchOpCode : public RegExpOpCode { +public: + MatchOpCode() : RegExpOpCode(OP_MATCH, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~MatchOpCode() override = default; + NO_COPY_SEMANTIC(MatchOpCode); + NO_MOVE_SEMANTIC(MatchOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class MatchEndOpCode : public RegExpOpCode { +public: + MatchEndOpCode() : RegExpOpCode(OP_MATCH_END, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~MatchEndOpCode() override = default; + NO_COPY_SEMANTIC(MatchEndOpCode); + NO_MOVE_SEMANTIC(MatchEndOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class LineStartOpCode : public RegExpOpCode { +public: + LineStartOpCode() : RegExpOpCode(OP_LINE_START, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~LineStartOpCode() override = default; + NO_COPY_SEMANTIC(LineStartOpCode); + NO_MOVE_SEMANTIC(LineStartOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class LineEndOpCode : public RegExpOpCode { +public: + LineEndOpCode() : RegExpOpCode(OP_LINE_END, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~LineEndOpCode() override = default; + NO_COPY_SEMANTIC(LineEndOpCode); + NO_MOVE_SEMANTIC(LineEndOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class WordBoundaryOpCode : public RegExpOpCode { +public: + WordBoundaryOpCode() : RegExpOpCode(OP_WORD_BOUNDARY, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~WordBoundaryOpCode() override = default; + NO_COPY_SEMANTIC(WordBoundaryOpCode); + NO_MOVE_SEMANTIC(WordBoundaryOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class NotWordBoundaryOpCode : public RegExpOpCode { +public: + NotWordBoundaryOpCode() : RegExpOpCode(OP_NOT_WORD_BOUNDARY, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~NotWordBoundaryOpCode() override = default; + NO_COPY_SEMANTIC(NotWordBoundaryOpCode); + NO_MOVE_SEMANTIC(NotWordBoundaryOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class AllOpCode : public RegExpOpCode { +public: + AllOpCode() : RegExpOpCode(OP_ALL, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~AllOpCode() override = default; + NO_COPY_SEMANTIC(AllOpCode); + NO_MOVE_SEMANTIC(AllOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class DotsOpCode : public RegExpOpCode { +public: + DotsOpCode() : RegExpOpCode(OP_DOTS, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~DotsOpCode() override = default; + NO_COPY_SEMANTIC(DotsOpCode); + NO_MOVE_SEMANTIC(DotsOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class RangeSet { +public: + RangeSet() = default; + explicit RangeSet(uint32_t value) + { + Insert(value, value); + } + explicit RangeSet(uint32_t start, uint32_t end) + { + Insert(start, end); + } + explicit RangeSet(const std::list> &rangeSet) + { + rangeSet_ = rangeSet; + } + ~RangeSet() = default; + + inline bool IsIntersect(uint64_t start, uint64_t end, uint64_t start1, uint64_t end1) const + { + return ((start1 > start) && (start1 < end)) || ((start > start1) && (start < end1)); + } + inline bool IsAdjacent(uint64_t start, uint64_t end, uint64_t start1, uint64_t end1) const + { + return ((end == start1 || (end + 1) == start1)) || ((end1 == start) || (end1 + 1 == start)); + } + + inline bool operator==(const RangeSet &other) const + { + return rangeSet_ == other.rangeSet_; + } + + inline bool IsContain(uint32_t value) const + { + for (auto range : rangeSet_) { + if (value >= range.first && value <= range.second) { + return true; + } + } + return false; + } + + inline uint32_t HighestValue() const + { + if (!rangeSet_.empty()) { + return rangeSet_.back().second; + } + return 0; + } + + RangeSet(RangeSet const &) = default; + RangeSet &operator=(RangeSet const &) = default; + RangeSet(RangeSet &&) = default; + RangeSet &operator=(RangeSet &&) = default; + + void Insert(uint32_t start, uint32_t end); + void Insert(const RangeSet &s1); + void Invert(bool isUtf16); + void Compress(); + +private: + friend class RangeOpCode; + friend class Range32OpCode; + std::list> rangeSet_{}; +}; + +class RangeOpCode : public RegExpOpCode { +public: + RangeOpCode() : RegExpOpCode(OP_RANGE, RegExpOpCode::OP_SIZE_ONE) {} + ~RangeOpCode() override = default; + NO_COPY_SEMANTIC(RangeOpCode); + NO_MOVE_SEMANTIC(RangeOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; + uint32_t InsertOpCode(DynChunk *buf, const RangeSet &rangeSet) const; +}; + +class MatchAheadOpCode : public RegExpOpCode { +public: + MatchAheadOpCode() : RegExpOpCode(OP_MATCH_AHEAD, RegExpOpCode::OP_SIZE_FIVE) {} + ~MatchAheadOpCode() override = default; + NO_COPY_SEMANTIC(MatchAheadOpCode); + NO_MOVE_SEMANTIC(MatchAheadOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const; +}; + +class NegativeMatchAheadOpCode : public RegExpOpCode { +public: + NegativeMatchAheadOpCode() : RegExpOpCode(OP_NEGATIVE_MATCH_AHEAD, RegExpOpCode::OP_SIZE_FIVE) {} + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const; + ~NegativeMatchAheadOpCode() override = default; + NO_COPY_SEMANTIC(NegativeMatchAheadOpCode); + NO_MOVE_SEMANTIC(NegativeMatchAheadOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class PrevOpCode : public RegExpOpCode { +public: + PrevOpCode() : RegExpOpCode(OP_PREV, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~PrevOpCode() override = default; + NO_COPY_SEMANTIC(PrevOpCode); + NO_MOVE_SEMANTIC(PrevOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class BackReferenceOpCode : public RegExpOpCode { +public: + BackReferenceOpCode() : RegExpOpCode(OP_BACKREFERENCE, RegExpOpCode::OP_SIZE_TWO) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~BackReferenceOpCode() override = default; + NO_COPY_SEMANTIC(BackReferenceOpCode); + NO_MOVE_SEMANTIC(BackReferenceOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class BackwardBackReferenceOpCode : public RegExpOpCode { +public: + BackwardBackReferenceOpCode() : RegExpOpCode(OP_BACKWARD_BACKREFERENCE, RegExpOpCode::OP_SIZE_TWO) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~BackwardBackReferenceOpCode() override = default; + NO_COPY_SEMANTIC(BackwardBackReferenceOpCode); + NO_MOVE_SEMANTIC(BackwardBackReferenceOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class Char32OpCode : public RegExpOpCode { +public: + Char32OpCode() : RegExpOpCode(OP_CHAR32, RegExpOpCode::OP_SIZE_FIVE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~Char32OpCode() override = default; + NO_COPY_SEMANTIC(Char32OpCode); + NO_MOVE_SEMANTIC(Char32OpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class Range32OpCode : public RegExpOpCode { +public: + Range32OpCode() : RegExpOpCode(OP_RANGE32, RegExpOpCode::OP_SIZE_ONE) {} + ~Range32OpCode() override = default; + NO_COPY_SEMANTIC(Range32OpCode); + NO_MOVE_SEMANTIC(Range32OpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; + uint32_t InsertOpCode(DynChunk *buf, const RangeSet &rangeSet) const; +}; +} // namespace ecmascript +} // namespace panda +#endif diff --git a/ecmascript/regexp/regexp_parser.cpp b/ecmascript/regexp/regexp_parser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b005ced9fae1568b764d12b6fcb99311b6396381 --- /dev/null +++ b/ecmascript/regexp/regexp_parser.cpp @@ -0,0 +1,1294 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/regexp/regexp_parser.h" + +#include "ecmascript/base/string_helper.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/regexp/regexp_opcode.h" +#include "libpandabase/utils/utils.h" +#include "securec.h" +#include "unicode/uniset.h" + +#define _NO_DEBUG_ + +namespace panda::ecmascript { +static RangeSet g_rangeD(0x30, 0x39); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static RangeSet g_rangeS({ + std::pair(0x0009, 0x000D), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0020, 0x0020), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x00A0, 0x00A0), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x1680, 0x1680), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x2000, 0x200A), // NOLINTNEXTLINE(readability-magic-numbers) + /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */ + /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */ + std::pair(0x2028, 0x2029), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x202F, 0x202F), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x205F, 0x205F), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x3000, 0x3000), // NOLINTNEXTLINE(readability-magic-numbers) + /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */ + std::pair(0xFEFF, 0xFEFF), // NOLINTNEXTLINE(readability-magic-numbers) +}); + +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static RangeSet g_rangeW({ + std::pair(0x0030, 0x0039), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0041, 0x005A), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x005F, 0x005F), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0061, 0x007A), // NOLINTNEXTLINE(readability-magic-numbers) +}); + +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static RangeSet g_regexpIdentifyStart({ + std::pair(0x0024, 0x0024), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0041, 0x005A), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0061, 0x007A), // NOLINTNEXTLINE(readability-magic-numbers) +}); + +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static RangeSet g_regexpIdentifyContinue({ + std::pair(0x0024, 0x0024), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0030, 0x0039), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0041, 0x005A), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0061, 0x007A), // NOLINTNEXTLINE(readability-magic-numbers) +}); + +void RegExpParser::Parse() +{ + // dynbuffer head init [size,capture_count,statck_count,flags] + buffer_.EmitU32(0); + buffer_.EmitU32(0); + buffer_.EmitU32(0); + buffer_.EmitU32(0); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Parse Pattern------\n"); + // Pattern[U, N]:: + // Disjunction[?U, ?N] + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + Advance(); + SaveStartOpCode saveStartOp; + int captureIndex = captureCount_++; + saveStartOp.EmitOpCode(&buffer_, captureIndex); + ParseDisjunction(false); + if (c0_ != KEY_EOF) { + ParseError("extraneous characters at the end"); + return; + } + SaveEndOpCode saveEndOp; + saveEndOp.EmitOpCode(&buffer_, captureIndex); + MatchEndOpCode matchEndOp; + matchEndOp.EmitOpCode(&buffer_, 0); + // dynbuffer head assignments + buffer_.PutU32(0, buffer_.size_); + buffer_.PutU32(NUM_CAPTURE__OFFSET, captureCount_); + buffer_.PutU32(NUM_STACK_OFFSET, stackCount_); + buffer_.PutU32(FLAGS_OFFSET, flags_); +#ifndef _NO_DEBUG_ + RegExpOpCode::DumpRegExpOpCode(std::cout, buffer_); +#endif +} + +void RegExpParser::ParseDisjunction(bool isBackward) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Parse Disjunction------\n"); + size_t start = buffer_.size_; + ParseAlternative(isBackward); + if (isError_) { + return; + } + do { + if (c0_ == '|') { + SplitNextOpCode splitOp; + uint32_t len = buffer_.size_ - start; + GotoOpCode gotoOp; + splitOp.InsertOpCode(&buffer_, start, len + gotoOp.GetSize()); + uint32_t pos = gotoOp.EmitOpCode(&buffer_, 0) - gotoOp.GetSize(); + Advance(); + ParseAlternative(isBackward); + gotoOp.UpdateOpPara(&buffer_, pos, buffer_.size_ - pos - gotoOp.GetSize()); + } + } while (c0_ != KEY_EOF && c0_ != ')'); +} + +uint32_t RegExpParser::ParseOctalLiteral() +{ + // For compatibility with some other browsers (not all), we parse + // up to three octal digits with a value below 256. + // ES#prod-annexB-LegacyOctalEscapeSequence + uint32_t value = c0_ - '0'; + Advance(); + if (c0_ >= '0' && c0_ <= '7') { + value = value * OCTAL_VALUE + c0_ - '0'; + Advance(); + if (value < OCTAL_VALUE_RANGE && c0_ >= '0' && c0_ <= '7') { + value = value * OCTAL_VALUE + c0_ - '0'; + Advance(); + } + } + return value; +} + +bool RegExpParser::ParseUnlimitedLengthHexNumber(uint32_t maxValue, uint32_t *value) +{ + uint32_t x = 0; + int d = HexValue(c0_); + if (d < 0) { + return false; + } + while (d >= 0) { + if (UNLIKELY(x > (std::numeric_limits::max() - d) / HEX_VALUE)) { + LOG_ECMA(FATAL) << "value overflow"; + return false; + } + x = x * HEX_VALUE + d; + if (x > maxValue) { + return false; + } + Advance(); + d = HexValue(c0_); + } + *value = x; + return true; +} + +// This parses RegExpUnicodeEscapeSequence as described in ECMA262. +bool RegExpParser::ParseUnicodeEscape(uint32_t *value) +{ + // Accept both \uxxxx and \u{xxxxxx} (if allowed). + // In the latter case, the number of hex digits between { } is arbitrary. + // \ and u have already been read. + if (c0_ == '{' && IsUtf16()) { + uint8_t *start = pc_ - 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + Advance(); + if (ParseUnlimitedLengthHexNumber(0x10FFFF, value)) { // NOLINTNEXTLINE(readability-magic-numbers) + if (c0_ == '}') { + Advance(); + return true; + } + } + pc_ = start; + Advance(); + return false; + } + // \u but no {, or \u{...} escapes not allowed. + bool result = ParseHexEscape(UNICODE_HEX_VALUE, value); + if (result && IsUtf16() && U16_IS_LEAD(*value) && c0_ == '\\') { + // Attempt to read trail surrogate. + uint8_t *start = pc_ - 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (*pc_ == 'u') { + Advance(UNICODE_HEX_ADVANCE); + uint32_t trail; + if (ParseHexEscape(UNICODE_HEX_VALUE, &trail) && U16_IS_TRAIL(trail)) { + *value = U16_GET_SUPPLEMENTARY((*value), (trail)); // NOLINTNEXTLINE(hicpp-signed-bitwise) + return true; + } + } + pc_ = start; + Advance(); + } + return result; +} + +bool RegExpParser::ParseHexEscape(int length, uint32_t *value) +{ + uint8_t *start = pc_ - 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint32_t val = 0; + for (int i = 0; i < length; ++i) { + uint32_t c = c0_; + int d = HexValue(c); + if (d < 0) { + pc_ = start; + Advance(); + return false; + } + val = val * HEX_VALUE + d; + Advance(); + } + *value = val; + return true; +} + +// NOLINTNEXTLINE(readability-function-size) +void RegExpParser::ParseAlternative(bool isBackward) +{ + size_t start = buffer_.size_; + while (c0_ != '|' && c0_ != KEY_EOF && c0_ != ')') { + if (isError_) { + return; + } + size_t atomBcStart = buffer_.GetSize(); + int captureIndex = 0; + bool isAtom = false; + switch (c0_) { + case '^': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion %c line start \n", c0_); + LineStartOpCode lineStartOp; + lineStartOp.EmitOpCode(&buffer_, 0); + Advance(); + } break; + case '$': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion %c line end \n", c0_); + LineEndOpCode lineEndOp; + lineEndOp.EmitOpCode(&buffer_, 0); + Advance(); + } break; + case '\\': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Escape %c \n", c0_); + Advance(); + switch (c0_) { + case 'b': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion %c \n", c0_); + WordBoundaryOpCode wordBoundaryOp; + wordBoundaryOp.EmitOpCode(&buffer_, 0); + Advance(); + } break; + case 'B': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion %c \n", c0_); + NotWordBoundaryOpCode notWordBoundaryOp; + notWordBoundaryOp.EmitOpCode(&buffer_, 0); + Advance(); + } break; + default: + isAtom = true; + int atomValue = ParseAtomEscape(isBackward); + if (atomValue != -1) { + if (IsIgnoreCase()) { + if (!IsUtf16()) { + atomValue = Canonicalize(atomValue, false); + } else { + icu::UnicodeSet set(atomValue, atomValue); + set.closeOver(USET_CASE_INSENSITIVE); + set.removeAllStrings(); + int32_t size = set.size(); + RangeOpCode rangeOp; + RangeSet rangeResult; + for (int32_t idx = 0; idx < size; idx++) { + int32_t uc = set.charAt(idx); + RangeSet curRange(uc); + rangeResult.Insert(curRange); + } + rangeOp.InsertOpCode(&buffer_, rangeResult); + break; + } + } + if (atomValue <= UINT16_MAX) { + CharOpCode charOp; + charOp.EmitOpCode(&buffer_, atomValue); + } else { + Char32OpCode charOp; + charOp.EmitOpCode(&buffer_, atomValue); + } + } + break; + } + break; + case '(': { + Advance(); + isAtom = ParseAssertionCapture(&captureIndex, isBackward); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + Advance(); + } break; + case '.': { + PrevOpCode prevOp; + if (isBackward) { + prevOp.EmitOpCode(&buffer_, 0); + } + if (IsDotAll()) { + AllOpCode allOp; + allOp.EmitOpCode(&buffer_, 0); + } else { + DotsOpCode dotsOp; + dotsOp.EmitOpCode(&buffer_, 0); + } + if (isBackward) { + prevOp.EmitOpCode(&buffer_, 0); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Atom %c match any \n", c0_); + isAtom = true; + Advance(); + } break; + case '[': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Atom %c match range \n", c0_); + isAtom = true; + PrevOpCode prevOp; + Advance(); + if (isBackward) { + prevOp.EmitOpCode(&buffer_, 0); + } + bool isInvert = false; + if (c0_ == '^') { + isInvert = true; + Advance(); + } + RangeSet rangeResult; + if (!ParseClassRanges(&rangeResult)) { + break; + } + if (isInvert) { + rangeResult.Invert(IsUtf16()); + } + uint32_t highValue = rangeResult.HighestValue(); + if (highValue <= UINT16_MAX) { + RangeOpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, rangeResult); + } else { + Range32OpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, rangeResult); + } + + if (isBackward) { + prevOp.EmitOpCode(&buffer_, 0); + } + } break; + case '*': + case '+': + case '?': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + ParseError("nothing to repeat"); + return; + case '{': { + uint8_t *begin = pc_ - 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + int dummy; + if (ParserIntervalQuantifier(&dummy, &dummy)) { + ParseError("nothing to repeat"); + return; + } + pc_ = begin; + Advance(); + } + [[fallthrough]]; + case '}': + case ']': + if (IsUtf16()) { + ParseError("syntax error"); + return; + } + [[fallthrough]]; + default: + // PatternCharacter + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("PatternCharacter %c\n", c0_); + isAtom = true; + { + PrevOpCode prevOp; + if (isBackward) { + prevOp.EmitOpCode(&buffer_, 0); + } + uint32_t matchedChar = c0_; + if (c0_ > (INT8_MAX + 1)) { + Prev(); + int i = 0; + UChar32 c; + int32_t length = end_ - pc_ + 1; + // NOLINTNEXTLINE(hicpp-signed-bitwise) + U8_NEXT(pc_, i, length, c); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + matchedChar = c; + pc_ += i; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + if (IsIgnoreCase()) { + matchedChar = Canonicalize(matchedChar, IsUtf16()); + } + if (matchedChar > UINT16_MAX) { + Char32OpCode charOp; + charOp.EmitOpCode(&buffer_, matchedChar); + } else { + CharOpCode charOp; + charOp.EmitOpCode(&buffer_, matchedChar); + } + if (isBackward) { + prevOp.EmitOpCode(&buffer_, 0); + } + } + Advance(); + break; + } + if (isAtom && !isError_) { + ParseQuantifier(atomBcStart, captureIndex, captureCount_ - 1); + } + if (isBackward) { + size_t end = buffer_.GetSize(); + size_t termSize = end - atomBcStart; + size_t moveSize = end - start; + buffer_.Expand(end + termSize); + if (memmove_s(buffer_.buf_ + start + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + termSize, // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + moveSize, + buffer_.buf_ + start, // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + moveSize) != EOK) { + LOG_ECMA(FATAL) << "memmove_s failed"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(buffer_.buf_ + start, termSize, buffer_.buf_ + end, termSize) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } + } +} + +int RegExpParser::FindGroupName(const CString &name) +{ + size_t len = 0; + size_t nameLen = name.size(); + const char *p = reinterpret_cast(groupNames_.buf_); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const char *bufEnd = reinterpret_cast(groupNames_.buf_) + groupNames_.size_; + int captureIndex = 1; + while (p < bufEnd) { + len = strlen(p); + if (len == nameLen && memcmp(name.c_str(), p, nameLen) == 0) { + return captureIndex; + } + p += len + 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + captureIndex++; + } + return -1; +} + +bool RegExpParser::ParseAssertionCapture(int *captureIndex, bool isBackward) +{ + bool isAtom = false; + do { + if (c0_ == '?') { + Advance(); + switch (c0_) { + // (?=Disjunction[?U, ?N]) + case '=': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion(?= Disjunction)\n"); + Advance(); + uint32_t start = buffer_.size_; + ParseDisjunction(isBackward); + MatchOpCode matchOp; + matchOp.EmitOpCode(&buffer_, 0); + MatchAheadOpCode matchAheadOp; + uint32_t len = buffer_.size_ - start; + matchAheadOp.InsertOpCode(&buffer_, start, len); + } break; + // (?!Disjunction[?U, ?N]) + case '!': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion(?! Disjunction)\n"); + uint32_t start = buffer_.size_; + Advance(); + ParseDisjunction(isBackward); + MatchOpCode matchOp; + matchOp.EmitOpCode(&buffer_, 0); + NegativeMatchAheadOpCode matchAheadOp; + uint32_t len = buffer_.size_ - start; + matchAheadOp.InsertOpCode(&buffer_, start, len); + } break; + case '<': + Advance(); + // (?<=Disjunction[?U, ?N]) + if (c0_ == '=') { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion(?<= Disjunction)\n"); + Advance(); + uint32_t start = buffer_.size_; + ParseDisjunction(true); + MatchOpCode matchOp; + matchOp.EmitOpCode(&buffer_, 0); + MatchAheadOpCode matchAheadOp; + uint32_t len = buffer_.size_ - start; + matchAheadOp.InsertOpCode(&buffer_, start, len); + // (?(&pc_); + if (!ParseGroupSpecifier(pp, name)) { + ParseError("GroupName Syntax error."); + return false; + } + if (FindGroupName(name) > 0) { + ParseError("Duplicate GroupName error."); + return false; + } + groupNames_.EmitStr(name.c_str()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("group name %s", name.c_str()); + Advance(); + goto parseCapture; // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto) + } + break; + // (?:Disjunction[?U, ?N]) + case ':': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Atom(?<: Disjunction)\n"); + isAtom = true; + Advance(); + ParseDisjunction(isBackward); + break; + default: + Advance(); + ParseError("? Syntax error."); + return false; + } + } else { + groupNames_.EmitChar(0); + parseCapture: + isAtom = true; + *captureIndex = captureCount_++; + SaveEndOpCode saveEndOp; + SaveStartOpCode saveStartOp; + if (isBackward) { + saveEndOp.EmitOpCode(&buffer_, *captureIndex); + } else { + saveStartOp.EmitOpCode(&buffer_, *captureIndex); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("capture start %d \n", *captureIndex); + ParseDisjunction(isBackward); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("capture end %d \n", *captureIndex); + if (isBackward) { + saveStartOp.EmitOpCode(&buffer_, *captureIndex); + } else { + saveEndOp.EmitOpCode(&buffer_, *captureIndex); + } + } + } while (c0_ != ')' && c0_ != KEY_EOF); + if (c0_ != ')') { + ParseError("capture syntax error"); + return false; + } + return isAtom; +} + +int RegExpParser::ParseDecimalDigits() +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Parse DecimalDigits------\n"); + int result = 0; + bool overflow = false; + while (true) { + if (c0_ < '0' || c0_ > '9') { + break; + } + if (!overflow) { + if (UNLIKELY(result > (INT32_MAX - c0_ + '0') / DECIMAL_DIGITS_ADVANCE)) { + overflow = true; + } else { + result = result * DECIMAL_DIGITS_ADVANCE + c0_ - '0'; + } + } + Advance(); + } + if (overflow) { + return INT32_MAX; + } + return result; +} + +bool RegExpParser::ParserIntervalQuantifier(int *pmin, int *pmax) +{ + // Quantifier:: + // QuantifierPrefix + // QuantifierPrefix? + // QuantifierPrefix:: + // * + // + + // ? + // {DecimalDigits} + // {DecimalDigits,} + // {DecimalDigits,DecimalDigits} + Advance(); + *pmin = ParseDecimalDigits(); + *pmax = *pmin; + switch (c0_) { + case ',': + Advance(); + if (c0_ == '}') { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("QuantifierPrefix{DecimalDigits,}\n"); + *pmax = INT32_MAX; + Advance(); + } else { + *pmax = ParseDecimalDigits(); + if (c0_ == '}') { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("QuantifierPrefix{DecimalDigits,DecimalDigits}\n"); + Advance(); + } else { + return false; + } + } + break; + case '}': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("QuantifierPrefix{DecimalDigits}\n"); + Advance(); + break; + default: + Advance(); + return false; + } + return true; +} + +void RegExpParser::ParseQuantifier(size_t atomBcStart, int captureStart, int captureEnd) +{ + int min = -1; + int max = -1; + bool isGreedy = true; + switch (c0_) { + case '*': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("QuantifierPrefix %c\n", c0_); + min = 0; + max = INT32_MAX; + Advance(); + break; + case '+': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("QuantifierPrefix %c\n", c0_); + min = 1; + max = INT32_MAX; + Advance(); + break; + case '?': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("QuantifierPrefix %c\n", c0_); + Advance(); + min = 0; + max = 1; + break; + case '{': { + uint8_t *start = pc_ - 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (!ParserIntervalQuantifier(&min, &max)) { + pc_ = start; + Advance(); // back to '{' + return; + } + if (min > max) { + ParseError("Invalid repetition count"); + return; + } + } break; + default: + break; + } + if (c0_ == '?') { + isGreedy = false; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Quantifier::QuantifierPrefix?\n"); + Advance(); + } else if (c0_ == '?' || c0_ == '+' || c0_ == '*' || c0_ == '{') { + ParseError("nothing to repeat"); + return; + } + if (min != -1 && max != -1) { + stackCount_++; + PushOpCode pushOp; + pushOp.InsertOpCode(&buffer_, atomBcStart); + atomBcStart += pushOp.GetSize(); + + if (captureStart != 0) { + SaveResetOpCode saveResetOp; + saveResetOp.InsertOpCode(&buffer_, atomBcStart, captureStart, captureEnd); + } + + // zero advance check + if (max == INT32_MAX) { + stackCount_++; + PushCharOpCode pushCharOp; + pushCharOp.InsertOpCode(&buffer_, atomBcStart); + CheckCharOpCode checkCharOp; + // NOLINTNEXTLINE(readability-magic-numbers) + checkCharOp.EmitOpCode(&buffer_, RegExpOpCode::GetRegExpOpCode(RegExpOpCode::OP_LOOP)->GetSize()); + } + + if (isGreedy) { + LoopGreedyOpCode loopOp; + loopOp.EmitOpCode(&buffer_, atomBcStart - buffer_.GetSize() - loopOp.GetSize(), min, max); + } else { + LoopOpCode loopOp; + loopOp.EmitOpCode(&buffer_, atomBcStart - buffer_.GetSize() - loopOp.GetSize(), min, max); + } + + if (min == 0) { + if (isGreedy) { + SplitNextOpCode splitNextOp; + splitNextOp.InsertOpCode(&buffer_, atomBcStart, buffer_.GetSize() - atomBcStart); + } else { + SplitFirstOpCode splitFirstOp; + splitFirstOp.InsertOpCode(&buffer_, atomBcStart, buffer_.GetSize() - atomBcStart); + } + } + + PopOpCode popOp; + popOp.EmitOpCode(&buffer_); + } +} + +bool RegExpParser::ParseGroupSpecifier(const uint8_t **pp, CString &name) +{ + const uint8_t *p = *pp; + int c = *p; + while (c != '>') { + if (c < (INT8_MAX + 1)) { + if (name.empty()) { + if (!g_regexpIdentifyStart.IsContain(c)) { + return false; + } + } else { + if (!g_regexpIdentifyContinue.IsContain(c)) { + return false; + } + } + name += static_cast(c); + } + c = *++p; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + p++; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *pp = p; + return true; +} + +int RegExpParser::ParseCaptureCount(const char *groupName) +{ + const uint8_t *p; + int captureIndex = 1; + CString name; + for (p = base_; p < end_; p++) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + switch (*p) { + case '(': { + if (p[1] == '?') { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (p[CAPTURE_CONUT_ADVANCE - 1] == '<' && p[CAPTURE_CONUT_ADVANCE] != '!' && + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + p[CAPTURE_CONUT_ADVANCE] != '=') { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + p += CAPTURE_CONUT_ADVANCE; + if (groupName != nullptr) { + if (ParseGroupSpecifier(&p, name)) { + if (strcmp(name.c_str(), groupName) == 0) { + return captureIndex; + } + } + } + captureIndex++; + } + } else { + captureIndex++; + } + } break; + case '\\': + p++; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + break; + case '[': + while (p < end_ && *p != ']') { + if (*p == '\\') { + p++; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + p++; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + break; + default: + break; + } + } + return captureIndex; +} + +// NOLINTNEXTLINE(readability-function-size) +int RegExpParser::ParseAtomEscape(bool isBackward) +{ + // AtomEscape[U, N]:: + // DecimalEscape + // CharacterClassEscape[?U] + // CharacterEscape[?U] + // [+N]kGroupName[?U] + int result = -1; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Parse AtomEscape------\n"); + switch (c0_) { + case KEY_EOF: + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + ParseError("unexpected end"); + break; + // DecimalEscape + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("NonZeroDigit %c\n", c0_); + int capture = ParseDecimalDigits(); + if (capture > captureCount_ - 1 && capture > ParseCaptureCount(nullptr) - 1) { + ParseError("invalid backreference count"); + break; + } + if (isBackward) { + BackwardBackReferenceOpCode backReferenceOp; + backReferenceOp.EmitOpCode(&buffer_, capture); + } else { + BackReferenceOpCode backReferenceOp; + backReferenceOp.EmitOpCode(&buffer_, capture); + } + } break; + // CharacterClassEscape + case 'd': { + // [0-9] + RangeOpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, g_rangeD); + Advance(); + } break; + case 'D': { + // [^0-9] + RangeSet atomRange(g_rangeD); + atomRange.Invert(IsUtf16()); + Range32OpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, atomRange); + Advance(); + } break; + case 's': { + // [\f\n\r\t\v] + RangeOpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, g_rangeS); + Advance(); + } break; + case 'S': { + RangeSet atomRange(g_rangeS); + atomRange.Invert(IsUtf16()); + Range32OpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, atomRange); + Advance(); + } break; + case 'w': { + // [A-Za-z0-9] + RangeOpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, g_rangeW); + Advance(); + } break; + case 'W': { + // [^A-Za-z0-9] + RangeSet atomRange(g_rangeW); + atomRange.Invert(IsUtf16()); + Range32OpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, atomRange); + Advance(); + } break; + // P{UnicodePropertyValueExpression} + // p{UnicodePropertyValueExpression} + case 'P': + case 'p': + // [+N]kGroupName[?U] + case 'k': + default: + result = ParseCharacterEscape(); + break; + } + return result; +} + +int RegExpParser::ParseCharacterEscape() +{ + // CharacterEscape[U]:: + // ControlEscape + // c ControlLetter + // 0 [lookahead ∉ DecimalDigit] + // HexEscapeSequence + // RegExpUnicodeEscapeSequence[?U] + // IdentityEscape[?U] + uint32_t result = 0; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + switch (c0_) { + // ControlEscape + case 'f': + result = '\f'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ControlEscape %c\n", c0_); + Advance(); + break; + case 'n': + result = '\n'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ControlEscape %c\n", c0_); + Advance(); + break; + case 'r': + result = '\r'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ControlEscape %c\n", c0_); + Advance(); + break; + case 't': + result = '\t'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ControlEscape %c\n", c0_); + Advance(); + break; + case 'v': + result = '\v'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ControlEscape %c\n", c0_); + Advance(); + break; + // c ControlLetter + case 'c': + Advance(); + if ((c0_ >= 'A' && c0_ <= 'Z') || (c0_ >= 'a' && c0_ <= 'z')) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ControlLetter %c\n", c0_); + result = static_cast(c0_) & 0x1f; // NOLINTNEXTLINE(readability-magic-numbers) + Advance(); + } else { + if (!IsUtf16()) { + pc_--; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + result = '\\'; + } else { + ParseError("Invalid control letter"); + return -1; + } + } + break; + case '0': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("CharacterEscape 0 [lookahead ∉ DecimalDigit]\n"); + if (IsUtf16() && !(*pc_ >= '0' && *pc_ <= '9')) { // NOLINTNEXTLINE(readability-magic-numbers) + Advance(); + result = 0; + break; + } + [[fallthrough]]; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + if (IsUtf16()) { + // With /u, decimal escape is not interpreted as octal character code. + ParseError("Invalid class escape"); + return 0; + } + result = ParseOctalLiteral(); + break; + // ParseHexEscapeSequence + // ParseRegExpUnicodeEscapeSequence + case 'x': { + Advance(); + if (ParseHexEscape(UNICODE_HEX_ADVANCE, &result)) { + return result; + } + if (IsUtf16()) { + ParseError("Invalid class escape"); + return -1; + } + result = 'x'; + break; + } + case 'u': { + Advance(); + if (ParseUnicodeEscape(&result)) { + return result; + } + if (IsUtf16()) { + // With /u, invalid escapes are not treated as identity escapes. + ParseError("Invalid unicode escape"); + return 0; + } + // If \u is not followed by a two-digit hexadecimal, treat it + // as an identity escape. + result = 'u'; + } break; + // IdentityEscape[?U] + case '$': + case '(': + case ')': + case '*': + case '+': + case '.': + case '/': + case '?': + case '[': + case '\\': + case ']': + case '^': + case '{': + case '|': + case '}': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("IdentityEscape %c\n", c0_); + result = c0_; + Advance(); + break; + default: + if (IsUtf16()) { + ParseError("Invalid unicode escape"); + return 0; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("SourceCharacter %c\n", c0_); + result = c0_; + Advance(); + break; + } + return result; +} + +bool RegExpParser::ParseClassRanges(RangeSet *result) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Parse ClassRanges------\n"); + while (c0_ != ']') { + RangeSet s1; + uint32_t c1 = ParseClassAtom(&s1); + if (c1 == UINT32_MAX) { + ParseError("invalid class range"); + return false; + } + + int next_c0 = *pc_; + if (c0_ == '-' && next_c0 != ']') { + if (c1 == CLASS_RANGE_BASE) { + if (IsUtf16()) { + ParseError("invalid class range"); + return false; + } + result->Insert(s1); + continue; + } + Advance(); + RangeSet s2; + uint32_t c2 = ParseClassAtom(&s2); + if (c2 == UINT32_MAX) { + ParseError("invalid class range"); + return false; + } + if (c2 == CLASS_RANGE_BASE) { + if (IsUtf16()) { + ParseError("invalid class range"); + return false; + } + result->Insert(s2); + continue; + } + + if (c1 > c2) { + ParseError("invalid class range"); + return false; + } + if (IsIgnoreCase()) { + c1 = Canonicalize(c1, IsUtf16()); + c2 = Canonicalize(c2, IsUtf16()); + } + + result->Insert(c1, c2); + } else { + result->Insert(s1); + } + } + Advance(); + return true; +} + +uint32_t RegExpParser::ParseClassAtom(RangeSet *atom) +{ + uint32_t ret = UINT32_MAX; + switch (c0_) { + case '\\': { + Advance(); + ret = ParseClassEscape(atom); + } break; + case KEY_EOF: + break; + case 0: + if (pc_ >= end_) { + return UINT32_MAX; + } + [[fallthrough]]; + default: + uint32_t value = c0_; + int u16_size = 0; + if (c0_ > INT8_MAX) { // NOLINTNEXTLINE(readability-magic-numbers) + pc_ -= 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto u16_result = base::utf_helper::ConvertUtf8ToUtf16Pair(pc_, true); + value = u16_result.first; + u16_size = u16_result.second; + Advance(u16_size + 1); + } else { + Advance(); + } + if (IsIgnoreCase()) { + value = Canonicalize(value, IsUtf16()); + } + atom->Insert(RangeSet(value)); + ret = value; + break; + } + return ret; +} + +int RegExpParser::ParseClassEscape(RangeSet *atom) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Parse ClassEscape------\n"); + int result = -1; + switch (c0_) { + case 'b': + Advance(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ClassEscape %c", 'b'); + result = '\b'; + atom->Insert(RangeSet(static_cast('\b'))); + break; + case '-': + Advance(); + result = '-'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ClassEscape %c", '-'); + atom->Insert(RangeSet(static_cast('-'))); + break; + // CharacterClassEscape + case 'd': + case 'D': + result = CLASS_RANGE_BASE; + atom->Insert(g_rangeD); + if (c0_ == 'D') { + atom->Invert(IsUtf16()); + } + Advance(); + break; + case 's': + case 'S': + result = CLASS_RANGE_BASE; + atom->Insert(g_rangeS); + if (c0_ == 'S') { + atom->Invert(IsUtf16()); + } + Advance(); + break; + case 'w': + case 'W': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ClassEscape::CharacterClassEscape %c\n", c0_); + result = CLASS_RANGE_BASE; + atom->Insert(g_rangeW); + if (c0_ == 'W') { + atom->Invert(IsUtf16()); + } + Advance(); + break; + // P{UnicodePropertyValueExpression} + // p{UnicodePropertyValueExpression} + case 'P': + case 'p': + Advance(); + if (c0_ == '{') { + Advance(); + bool isValue = false; + ParseUnicodePropertyValueCharacters(&isValue); + if (!isValue) { + ParseUnicodePropertyValueCharacters(&isValue); + } + } + break; + default: + result = ParseCharacterEscape(); + int value = result; + if (IsIgnoreCase()) { + value = Canonicalize(value, IsUtf16()); + } + atom->Insert(RangeSet(static_cast(value))); + break; + } + return result; +} + +void RegExpParser::ParseUnicodePropertyValueCharacters(bool *isValue) +{ + if ((c0_ >= 'A' && c0_ <= 'Z') || (c0_ >= 'a' && c0_ <= 'z')) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("UnicodePropertyCharacter::ControlLetter %c\n", c0_); + Advance(); + } else if (c0_ == '-') { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("UnicodePropertyCharacter:: - \n"); + Advance(); + } else if (c0_ >= '0' && c0_ <= '9') { + *isValue = true; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("UnicodePropertyValueCharacter::DecimalDigit %c\n", c0_); + Advance(); + } else if (*isValue && c0_ == '}') { + Advance(); + return; + } else if (!*isValue && c0_ == '=') { + Advance(); + return; + } + ParseUnicodePropertyValueCharacters(isValue); +} + +// NOLINTNEXTLINE(cert-dcl50-cpp) +void RegExpParser::PrintF(const char *fmt, ...) +{ +#ifndef _NO_DEBUG_ + va_list args; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,) + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +#else + (void)fmt; +#endif +} + +void RegExpParser::ParseError(const char *errorMessage) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("error: "); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF(errorMessage); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("\n"); + SetIsError(); + size_t length = strlen(errorMessage) + 1; + if (memcpy_s(errorMsg_, length, errorMessage, length) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/regexp/regexp_parser.h b/ecmascript/regexp/regexp_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..751f9f716bb6689c9b3bf31a97ab733812b0afb8 --- /dev/null +++ b/ecmascript/regexp/regexp_parser.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMA_REGEXP_PARSER_H +#define PANDA_RUNTIME_ECMA_REGEXP_PARSER_H + +#include +#include +#include +#include "ecmascript/mem/chunk.h" +#include "ecmascript/regexp/dyn_chunk.h" +#include "ecmascript/regexp/regexp_opcode.h" +#include "unicode/stringpiece.h" +#include "unicode/uchar.h" +#include "unicode/utf16.h" +#include "unicode/utf8.h" +#include "unicode/utypes.h" + +namespace panda::ecmascript { +class RegExpParser { +public: + static constexpr auto FLAG_GLOBAL = (1U << 0U); + static constexpr auto FLAG_IGNORECASE = (1U << 1U); + static constexpr auto FLAG_MULTILINE = (1U << 2U); + static constexpr auto FLAG_DOTALL = (1U << 3U); + static constexpr auto FLAG_UTF16 = (1U << 4U); + static constexpr auto FLAG_STICKY = (1U << 5U); + static const int KEY_EOF = -1; + static constexpr int CLASS_RANGE_BASE = 0x40000000; + static constexpr uint32_t NUM_CAPTURE__OFFSET = 4; + static constexpr uint32_t NUM_STACK_OFFSET = 8; + static constexpr uint32_t OCTAL_VALUE = 8; + static constexpr uint32_t OCTAL_VALUE_RANGE = 32; + static constexpr uint32_t HEX_VALUE = 16; + static constexpr int32_t DECIMAL_DIGITS_ADVANCE = 10; + static constexpr uint32_t FLAGS_OFFSET = 12; + static constexpr uint32_t OP_START_OFFSET = 16; + static constexpr uint32_t UNICODE_HEX_VALUE = 4; + static constexpr uint32_t UNICODE_HEX_ADVANCE = 2; + static constexpr uint32_t CAPTURE_CONUT_ADVANCE = 3; + + explicit RegExpParser(Chunk *chunk) + : base_(nullptr), + pc_(nullptr), + end_(nullptr), + flags_(0), + c0_(KEY_EOF), + captureCount_(0), + stackCount_(0), + isError_(false), + buffer_(chunk), + groupNames_(chunk) + { + } + + ~RegExpParser() + { + Clear(); + } + + NO_COPY_SEMANTIC(RegExpParser); + NO_MOVE_SEMANTIC(RegExpParser); + + inline void Init(char *source, size_t length, uint32_t flags) + { + pc_ = reinterpret_cast(source); + base_ = pc_; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + end_ = reinterpret_cast(source) + length - 1; + flags_ = flags; + } + + void Parse(); + void ParseDisjunction(bool isBackward); + void ParseAlternative(bool isBackward); + bool ParseAssertionCapture(int *captureIndex, bool isBackward); + void ParseQuantifier(size_t atomBcStart, int captureStart, int captureEnd); + int ParseDecimalDigits(); + int ParseAtomEscape(bool isBackward); + int ParseCharacterEscape(); + bool ParseGroupSpecifier(const uint8_t **pp, CString &name); + int ParseCaptureCount(const char *groupName); + bool ParseClassRanges(RangeSet *result); + void ParseNonemptyClassRangesNoDash(DynChunk *buffer); + uint32_t ParseClassAtom(RangeSet *atom); + int ParseClassEscape(RangeSet *atom); + void ParseError(const char *errorMessage); + void ParseUnicodePropertyValueCharacters(bool *isValue); + int FindGroupName(const CString &name); + uint32_t ParseOctalLiteral(); + bool ParseHexEscape(int length, uint32_t *value); + bool ParseUnlimitedLengthHexNumber(uint32_t maxValue, uint32_t *value); + bool ParseUnicodeEscape(uint32_t *value); + bool ParserIntervalQuantifier(int *pmin, int *pmax); + + inline bool IsError() const + { + return isError_; + } + + inline uint8_t *GetOriginBuffer() const + { + return buffer_.buf_; + } + + inline size_t GetOriginBufferSize() const + { + return buffer_.size_; + } + + inline CString GetErrorMsg() const + { + if (isError_) { + return CString(errorMsg_); + } + return CString(""); + } + + inline bool IsGlobal() const + { + return (flags_ & FLAG_GLOBAL) != 0; + } + + inline bool IsIgnoreCase() const + { + return (flags_ & FLAG_IGNORECASE) != 0; + } + + inline bool IsMultiline() const + { + return (flags_ & FLAG_MULTILINE) != 0; + } + + inline bool IsDotAll() const + { + return (flags_ & FLAG_DOTALL) != 0; + } + + inline bool IsUtf16() const + { + return (flags_ & FLAG_UTF16) != 0; + } + + inline bool IsStick() const + { + return (flags_ & FLAG_STICKY) != 0; + } + + inline static int Canonicalize(int c, bool isUnicode) + { + if (c < TMP_BUF_SIZE) { // NOLINTNEXTLINE(readability-magic-numbers) + if (c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + } else { + if (isUnicode) { + c = u_toupper(static_cast(c)); + } + } + return c; + } + +private: + friend class RegExpExecutor; + static constexpr int TMP_BUF_SIZE = 128; + void Clear() + { + base_ = nullptr; + pc_ = nullptr; + end_ = nullptr; + c0_ = KEY_EOF; + isError_ = false; + } + + void Advance() + { + if (pc_ <= end_) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + c0_ = *pc_++; + } else { + c0_ = KEY_EOF; + } + } + + void Advance(int offset) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + pc_ += offset - 1; + Advance(); + } + + void Prev() + { + if (pc_ >= base_) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + c0_ = *pc_--; + } else { + c0_ = KEY_EOF; + } + } + + void SetIsError() + { + isError_ = true; + } + + void PrintF(const char *fmt, ...); + uint8_t *base_; + uint8_t *pc_; + uint8_t *end_; + uint32_t flags_; + int c0_; + int captureCount_; + int stackCount_; + bool isError_; + char errorMsg_[TMP_BUF_SIZE] = {0}; // NOLINTNEXTLINE(modernize-avoid-c-arrays) + DynChunk buffer_; + DynChunk groupNames_; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMA_REGEXP_PARSER_H \ No newline at end of file diff --git a/ecmascript/regexp/regexp_parser_cache.cpp b/ecmascript/regexp/regexp_parser_cache.cpp new file mode 100644 index 0000000000000000000000000000000000000000..db54a7d590e541f08ff9df310c016c7b28ea056b --- /dev/null +++ b/ecmascript/regexp/regexp_parser_cache.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/regexp/regexp_parser_cache.h" + +namespace panda::ecmascript { +RegExpParserCache::RegExpParserCache() +{ + Clear(); +} + +RegExpParserCache::~RegExpParserCache() +{ + Clear(); +} + +void RegExpParserCache::Clear() +{ + for (ParserKey &info : info_) { + info.pattern_ = nullptr; + info.flags_ = UINT32_MAX; // flags cannot be UINT32_MAX, so it means invalid. + info.codeBuffer_ = JSTaggedValue::Hole(); + info.bufferSize_ = 0; + } +} + +size_t RegExpParserCache::GetHash(EcmaString *pattern, const uint32_t flags) +{ + return (pattern->GetHashcode() ^ flags) % CACHE_SIZE; +} + +std::pair RegExpParserCache::GetCache(EcmaString *pattern, const uint32_t flags) +{ + size_t hash = GetHash(pattern, flags); + ParserKey &info = info_[hash]; + if (info.flags_ != flags || !EcmaString::StringsAreEqual(info.pattern_, pattern)) { + return std::pair(JSTaggedValue::Hole(), 0); + } + return std::pair(info.codeBuffer_, info.bufferSize_); +} + +void RegExpParserCache::SetCache(EcmaString *pattern, const uint32_t flags, + const JSTaggedValue codeBuffer, const size_t bufferSize) +{ + size_t hash = GetHash(pattern, flags); + ParserKey &info = info_[hash]; + info.pattern_ = pattern; + info.flags_ = flags; + info.codeBuffer_ = codeBuffer; + info.bufferSize_ = bufferSize; +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/regexp/regexp_parser_cache.h b/ecmascript/regexp/regexp_parser_cache.h new file mode 100644 index 0000000000000000000000000000000000000000..e9cbf0cdbe9cd0d5c14e861205c7da12ed165c0b --- /dev/null +++ b/ecmascript/regexp/regexp_parser_cache.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMA_REGEXP_PARSER_CACHE_H +#define PANDA_RUNTIME_ECMA_REGEXP_PARSER_CACHE_H + +#include + +#include "ecmascript/ecma_string.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class RegExpParserCache { +public: + RegExpParserCache(); + ~RegExpParserCache(); + static constexpr size_t CACHE_SIZE = 128; + + bool IsInCache(EcmaString *pattern, const uint32_t flags); + std::pair GetCache(EcmaString *pattern, const uint32_t flags); + void SetCache(EcmaString *pattern, const uint32_t flags, const JSTaggedValue codeBuffer, const size_t bufferSize); + void Clear(); + +private: + size_t GetHash(EcmaString *pattern, const uint32_t flags); + + struct ParserKey { + EcmaString *pattern_{nullptr}; + uint32_t flags_{UINT32_MAX}; + JSTaggedValue codeBuffer_{JSTaggedValue::Hole()}; + size_t bufferSize_{0}; + }; + + std::array info_{}; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMA_REGEXP_PARSER_CACHE_H diff --git a/ecmascript/regexp/tests/BUILD.gn b/ecmascript/regexp/tests/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..9c6abc0df9238df35634dfd2c978cd7cdeca8d17 --- /dev/null +++ b/ecmascript/regexp/tests/BUILD.gn @@ -0,0 +1,78 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//ark/js_runtime/test/test_helper.gni") +import("//build/test.gni") + +module_output_path = "ark/js_runtime" + +host_unittest_action("DynChunkTest") { + module_out_path = module_output_path + + sources = [ + # test file + "dyn_chunk_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("RegexpTest") { + module_out_path = module_output_path + + sources = [ + # test file + "regexp_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +group("unittest") { + testonly = true + deps = [ + ":DynChunkTest", + ":RegexpTest", + ] +} + +group("host_unittest") { + testonly = true + deps = [ + ":DynChunkTestAction(${host_toolchain})", + ":RegexpTestAction(${host_toolchain})", + ] +} diff --git a/ecmascript/regexp/tests/dyn_chunk_test.cpp b/ecmascript/regexp/tests/dyn_chunk_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a3a749eb01f92d012eb2911aa0734782c4046198 --- /dev/null +++ b/ecmascript/regexp/tests/dyn_chunk_test.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tests/test_helper.h" + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/regexp/dyn_chunk.h" + +namespace panda::test { +using namespace panda::ecmascript; + +class DynChunkTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + chunk_ = thread->GetEcmaVM()->GetChunk(); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + JSThread *thread {nullptr}; + EcmaHandleScope *scope {nullptr}; + Chunk *chunk_ {nullptr}; +}; + +HWTEST_F_L0(DynChunkTest, EmitAndGet) +{ + DynChunk dynChunk = DynChunk(chunk_); + dynChunk.EmitChar(65); + dynChunk.EmitU16(66); + dynChunk.EmitU32(67); + ASSERT_EQ(dynChunk.GetSize(), 7); + ASSERT_EQ(dynChunk.GetAllocatedSize(), DynChunk::ALLOCATE_MIN_SIZE); + ASSERT_EQ(dynChunk.GetError(), false); + dynChunk.Insert(1, 1); + uint32_t val1 = dynChunk.GetU8(0); + uint32_t val2 = dynChunk.GetU16(2); + uint32_t val3 = dynChunk.GetU32(4); + ASSERT_EQ(val1, 65); + ASSERT_EQ(val2, 66); + ASSERT_EQ(val3, 67); +} + +HWTEST_F_L0(DynChunkTest, EmitSelfAndGet) +{ + DynChunk dynChunk = DynChunk(chunk_); + dynChunk.EmitChar(65); + dynChunk.EmitSelf(0, 1); + ASSERT_EQ(dynChunk.GetSize(), 2); + ASSERT_EQ(dynChunk.GetAllocatedSize(), DynChunk::ALLOCATE_MIN_SIZE); + ASSERT_EQ(dynChunk.GetError(), false); + uint32_t val1 = dynChunk.GetU8(0); + uint32_t val2 = dynChunk.GetU8(1); + ASSERT_EQ(val1, 65); + ASSERT_EQ(val2, 65); +} + +HWTEST_F_L0(DynChunkTest, EmitStrAndGet) +{ + DynChunk dynChunk = DynChunk(chunk_); + dynChunk.EmitStr("abc"); + ASSERT_EQ(dynChunk.GetSize(), 4); + ASSERT_EQ(dynChunk.GetAllocatedSize(), DynChunk::ALLOCATE_MIN_SIZE); + ASSERT_EQ(dynChunk.GetError(), false); + uint32_t val1 = dynChunk.GetU8(0); + uint32_t val2 = dynChunk.GetU8(1); + uint32_t val3 = dynChunk.GetU8(2); + uint32_t val4 = dynChunk.GetU8(3); + ASSERT_EQ(val1, 97); + ASSERT_EQ(val2, 98); + ASSERT_EQ(val3, 99); + ASSERT_EQ(val4, 0); +} +} // namespace panda::test diff --git a/ecmascript/regexp/tests/regexp_test.cpp b/ecmascript/regexp/tests/regexp_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..299442c30ba20893754b0bb2fda8b9406722b167 --- /dev/null +++ b/ecmascript/regexp/tests/regexp_test.cpp @@ -0,0 +1,2023 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/regexp/regexp_executor.h" +#include "ecmascript/regexp/regexp_parser.h" +#include "ecmascript/tests/test_helper.h" + +namespace panda::test { +using namespace panda::ecmascript; +using MatchResult = RegExpExecutor::MatchResult; + +class RegExpTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + chunk_ = thread->GetEcmaVM()->GetChunk(); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + bool IsValidAlphaEscapeInAtom(char s) const + { + switch (s) { + // Assertion [U] :: \b + case 'b': + // Assertion [U] :: \B + case 'B': + // ControlEscape :: one of f n r t v + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + // CharacterClassEscape :: one of d D s S w W + case 'd': + case 'D': + case 's': + case 'S': + case 'w': + case 'W': + return true; + default: + return false; + } + } + + bool IsValidAlphaEscapeInClass(char s) const + { + switch (s) { + // ClassEscape[U] :: b + case 'b': + // ControlEscape :: one of f n r t v + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + // CharacterClassEscape :: one of d D s S w W + case 'd': + case 'D': + case 's': + case 'S': + case 'w': + case 'W': + return true; + default: + return false; + } + } + +protected: + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + Chunk *chunk_ {nullptr}; +}; + +HWTEST_F_L0(RegExpTest, ParseError1) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("0{2,1}"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError2) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("^[z-a]$"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError3) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError4) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a**"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError5) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a***"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError6) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a**"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError7) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a++"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError8) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a+++"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError9) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a???"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError10) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a????"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError11) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("*a"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError12) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("**a"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError13) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("+a"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError14) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("++a"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError15) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("?a"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError16) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("??a"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError17) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("x{1}{1,}"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError18) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("x{1,2}{1}"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError19) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("x{1,}{1}"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError20) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("x{0,1}{1,}"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError21) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[b-ac-e]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError22) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\10b-G]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError23) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\0b-G]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError24) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("("); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError25) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source(")"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError26) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("{"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError27) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("}"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError28) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("["); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError29) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError30) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\c"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError31) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\c\024"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError32) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\c]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError33) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\c\024]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError34) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\d-a]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError35) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\s-a]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError36) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\s-\\w]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError37) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[a-\\w]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError38) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\{"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError39) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\/"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError40) +{ + for (char cu = 0x41; cu <= 0x5a; ++cu) { + if (!IsValidAlphaEscapeInAtom(cu)) { + CString source("\\"); + source += CString(&cu, 1); + RegExpParser parser = RegExpParser(chunk_); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); + } + } + for (char cu = 0x61; cu <= 0x7a; ++cu) { + if (!IsValidAlphaEscapeInAtom(cu)) { + CString source("\\"); + source += CString(&cu, 1); + RegExpParser parser = RegExpParser(chunk_); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); + } + } + for (char cu = 0x41; cu <= 0x5a; ++cu) { + CString source("[\\"); + if (!IsValidAlphaEscapeInAtom(cu)) { + source += CString(&cu, 1); + source += CString("]"); + RegExpParser parser = RegExpParser(chunk_); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); + } + } + for (char cu = 0x61; cu <= 0x7a; ++cu) { + CString source("[\\"); + if (!IsValidAlphaEscapeInAtom(cu)) { + source += CString(&cu, 1); + source += CString("]"); + RegExpParser parser = RegExpParser(chunk_); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); + } + } +} + +HWTEST_F_L0(RegExpTest, ParseError44) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\1"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError45) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\1]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError46) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\00"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseError47) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\00]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseNoError1) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a{10,2147483648}"); // 2^31 + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseNoError2) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a{10,4294967306}"); // 2^32+10 + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("ab"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("abc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("ab"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(((ab)|(cd)|(de))|((ef)|(gh)|(jk)))"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("cabd"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 10); + JSHandle str = factory->NewFromString("ab"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str) == 0); + ASSERT_TRUE(result.captures_[3].second->Compare(*str) == 0); + ASSERT_TRUE(result.captures_[4].first); + ASSERT_TRUE(result.captures_[5].first); + ASSERT_TRUE(result.captures_[6].first); + ASSERT_TRUE(result.captures_[7].first); + ASSERT_TRUE(result.captures_[8].first); + ASSERT_TRUE(result.captures_[9].first); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec3) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(aa|aabaac|ba|b|c)*"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("aabaac"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromString("aaba"); + JSHandle str2 = factory->NewFromString("ba"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec4) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a*"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("aabaac"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("aa"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec5) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a?"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("b"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString(""); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec6) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(z)((a+)?(b+)?(c))*"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("zaacbbbcac"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 6); + JSHandle str1 = factory->NewFromString("zaacbbbcac"); + JSHandle str2 = factory->NewFromString("z"); + JSHandle str3 = factory->NewFromString("ac"); + JSHandle str4 = factory->NewFromString("a"); + JSHandle str5 = factory->NewFromString("c"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); + ASSERT_TRUE(result.captures_[3].second->Compare(*str4) == 0); + ASSERT_TRUE(result.captures_[4].first); + ASSERT_TRUE(result.captures_[5].second->Compare(*str5) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec7) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("^abc"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 4); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("ab\nabc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("abc"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec8) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("abc$"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 4); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("ab\nabc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("abc"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec9) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("er\\B"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("erv"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("er"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec10) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("d\\b"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("bad good"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("d"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec11) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("."); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("\na"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("a"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec12) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("."); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 8); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("\n"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("\n"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec13) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("abc"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 4); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("\naabc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("abc"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec14) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("abc"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 4); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("\nbbabc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("abc"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec15) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a(?=a)"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("aabc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("a"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec16) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("abc"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("ABC"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("ABC"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec17) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a\\n"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("a\n"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("a\n"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec18) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a(?=a)"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("ababc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_FALSE(ret); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec19) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a(?!a)"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("ababc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("a"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec20) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(?=(a+))"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("baaabac"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromString(""); + JSHandle str2 = factory->NewFromString("aaa"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec21) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a(?=a(?=b))"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("caab"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("a"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec22) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source(".+:"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("aaaa:aa"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("aaaa:"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec23) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a(?<=a(?(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("caab"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("a"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec24) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a(?<=ab(?(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("caab"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_FALSE(ret); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec25) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(?<=(ab))"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("cabab"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromString(""); + JSHandle str2 = factory->NewFromString("ab"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec26) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("[a-z]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("A"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("A"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec27) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("[^a-b]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("Z"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("Z"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec28) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("\\s"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("\n"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("\n"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec29) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("()|"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input(""); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str = factory->NewFromString(""); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec30) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("|()"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input(""); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str = factory->NewFromString(""); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); + ASSERT_TRUE(result.captures_[1].first); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec31) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a(a|b)\\1"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("aabb"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromString("abb"); + JSHandle str2 = factory->NewFromString("b"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec32) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(a(a|b))\\2"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("aabb"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 3); + JSHandle str1 = factory->NewFromString("abb"); + JSHandle str2 = factory->NewFromString("ab"); + JSHandle str3 = factory->NewFromString("b"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec33) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("qya+"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("qyqya"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("qya"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec34) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("qy(?=\\s+)"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("qyqy "); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("qy"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec35) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(\\d{4})-(\\d{2})-(\\d{2})"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("xx2021-01-09"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 4); + JSHandle str1 = factory->NewFromString("2021-01-09"); + JSHandle str2 = factory->NewFromString("2021"); + JSHandle str3 = factory->NewFromString("01"); + JSHandle str4 = factory->NewFromString("09"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); + ASSERT_TRUE(result.captures_[3].second->Compare(*str4) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec36) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("quick\\s(brown).+?(jumps)"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("The Quick Brown Fox Jumps Over The Lazy Dog"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 3); + JSHandle str1 = factory->NewFromString("Quick Brown Fox Jumps"); + JSHandle str2 = factory->NewFromString("Brown"); + JSHandle str3 = factory->NewFromString("Jumps"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec37) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(ab){1,2}?c"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("abABc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromString("abABc"); + JSHandle str2 = factory->NewFromString("AB"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec38) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("^(([a-z]+)*[a-z]\\.)+[a-z]{2,}$"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("www.netscape.com"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 3); + JSHandle str1 = factory->NewFromString("www.netscape.com"); + JSHandle str2 = factory->NewFromString("netscape."); + JSHandle str3 = factory->NewFromString("netscap"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec39) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(a*)b\\1+"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("baaaac"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromString("b"); + JSHandle str2 = factory->NewFromString(""); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec40) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a*?"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("ab"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString(""); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec41) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(.*?)a(?!(a+)b\\2c)\\2(.*)"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("baaabaac"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 4); + JSHandle str1 = factory->NewFromString("baaabaac"); + JSHandle str2 = factory->NewFromString("ba"); + JSHandle str3 = factory->NewFromString("abaac"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].first); + ASSERT_TRUE(result.captures_[3].second->Compare(*str3) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec42) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("[a-c\\d]+"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("\n\n\\abc324234"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("abc324234"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec43) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\d][\n][^\\d]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("line1\nline2"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("1\nl"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec44) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source(".[\b]."); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("abc\bdef"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("c\bd"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec45) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("[^\b]+"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("easy\bto\u0008ride"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("easy"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec46) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("([\\S]+([ \t]+[\\S]+)*)[ \t]*=[ \t]*[\\S]+"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("Course_Creator = Test"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 3); + JSHandle str1 = factory->NewFromString("Course_Creator = Test"); + JSHandle str2 = factory->NewFromString("Course_Creator"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_FALSE(result.captures_[1].first); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].first); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec47) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("[^o]t\\b"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("pilOt\nsoviet robot\topenoffice"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("et"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec49) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(a(b)\\4(5)(5))"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("ab55"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 5); + JSHandle str1 = factory->NewFromString("ab55"); + JSHandle str2 = factory->NewFromString("ab55"); + JSHandle str3 = factory->NewFromString("b"); + JSHandle str4 = factory->NewFromString("5"); + JSHandle str5 = factory->NewFromString("5"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); + ASSERT_TRUE(result.captures_[3].second->Compare(*str4) == 0); + ASSERT_TRUE(result.captures_[4].second->Compare(*str5) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec50) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(?\\d{4})-(?\\d{2}-(?\\d\\d))"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("2020-12-31"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 4); + JSHandle str1 = factory->NewFromString("2020-12-31"); + JSHandle str2 = factory->NewFromString("2020"); + JSHandle str3 = factory->NewFromString("12-31"); + JSHandle str4 = factory->NewFromString("31"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); + ASSERT_TRUE(result.captures_[3].second->Compare(*str4) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec51) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\u0000"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + std::u16string input(u"\u0000"); + bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length() + 1, + parser.GetOriginBuffer(), true); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec52) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(aa).+\\1"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("aabcdaabcd"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromString("aabcdaa"); + JSHandle str2 = factory->NewFromString("aa"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec53) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("\\x01"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + std::u16string input(u"\u0001"); + bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), + parser.GetOriginBuffer(), true); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("\u0001"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec54) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\bot"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("pilot\nsoviet robot\topenoffice"); + bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), + parser.GetOriginBuffer(), false); + ASSERT_FALSE(ret); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec55) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("e\\b"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("c\u0065"); + bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), + parser.GetOriginBuffer(), false); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("e"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec56) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a啊"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + std::u16string input(u"a啊"); + bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), + parser.GetOriginBuffer(), true); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("a啊"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec57) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\udf06"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + char16_t data[] = {0xd834, 0xdf06}; + bool ret = executor.Execute(reinterpret_cast(data), 0, 2, parser.GetOriginBuffer(), true); + ASSERT_FALSE(ret); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec58) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("\\udf06"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + char16_t data[] = {0xd834, 0xdf06}; + bool ret = executor.Execute(reinterpret_cast(data), 0, 2, parser.GetOriginBuffer(), true); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + char16_t data1[] = {0xdf06}; + JSHandle str = factory->NewFromUtf16(reinterpret_cast(data1), 1); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, ParseAndExec59) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("\\v"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("\u000B"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("\u000B"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +HWTEST_F_L0(RegExpTest, RangeSet1) +{ + std::list> listInput = { + std::make_pair(1, 1), + std::make_pair(2, 2), + std::make_pair(3, 3), + }; + std::list> listExpected = { + std::make_pair(1, 5), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Insert(4, 5); + rangeResult.Compress(); + EXPECT_EQ(rangeResult, rangeExpected); +} + +HWTEST_F_L0(RegExpTest, RangeSet2) +{ + std::list> listExpected = { + std::make_pair(4, 5), + }; + RangeSet rangeResult; + RangeSet rangeExpected(listExpected); + rangeResult.Insert(4, 5); + rangeResult.Compress(); + EXPECT_EQ(rangeResult, rangeExpected); +} + +HWTEST_F_L0(RegExpTest, RangeSet3) +{ + std::list> listInput = { + std::make_pair(2, 2), + }; + std::list> listExpected = { + std::make_pair(1, 5), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Insert(1, 5); + rangeResult.Compress(); + EXPECT_EQ(rangeResult, rangeExpected); +} + +HWTEST_F_L0(RegExpTest, RangeSet4) +{ + std::list> listInput = { + std::make_pair(1, 5), + }; + std::list> listExpected = { + std::make_pair(1, 5), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Insert(2, 4); + rangeResult.Compress(); + EXPECT_EQ(rangeResult, rangeExpected); +} + +HWTEST_F_L0(RegExpTest, RangeSet5) +{ + std::list> listInput = { + std::make_pair(1, 2), + std::make_pair(9, UINT16_MAX), + }; + std::list> listExpected = { + std::make_pair(1, 2), + std::make_pair(4, 7), + std::make_pair(9, UINT16_MAX), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Insert(4, 7); + rangeResult.Compress(); + EXPECT_EQ(rangeResult, rangeExpected); +} + +HWTEST_F_L0(RegExpTest, RangeSet6) +{ + std::list> listExpected = { + std::make_pair(0, UINT16_MAX), + }; + RangeSet rangeResult; + RangeSet rangeExpected(listExpected); + rangeResult.Invert(false); + EXPECT_EQ(rangeResult, rangeExpected); +} + +HWTEST_F_L0(RegExpTest, RangeSet7) +{ + std::list> listInput = { + std::make_pair(1, 5), + }; + std::list> listExpected = { + std::make_pair(0, 0), + std::make_pair(6, UINT16_MAX), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Invert(false); + EXPECT_EQ(rangeResult, rangeExpected); +} + +HWTEST_F_L0(RegExpTest, RangeSet8) +{ + std::list> listInput = { + std::make_pair(1, 5), + std::make_pair(0xfffe, UINT16_MAX), + }; + std::list> listExpected = { + std::make_pair(0, 0), + std::make_pair(6, 0xfffd), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Invert(false); + EXPECT_EQ(rangeResult, rangeExpected); +} + +HWTEST_F_L0(RegExpTest, RangeSet9) +{ + std::list> listInput = { + std::make_pair(0, 5), + std::make_pair(0xfffe, 0xfffe), + }; + std::list> listExpected = { + std::make_pair(6, 0xfffd), + std::make_pair(UINT16_MAX, UINT16_MAX), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Invert(false); + EXPECT_EQ(rangeResult, rangeExpected); +} + +HWTEST_F_L0(RegExpTest, RangeSet10) +{ + std::list> listInput = { + std::make_pair(0, UINT16_MAX), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected; + rangeResult.Invert(false); + EXPECT_EQ(rangeResult, rangeExpected); +} +} // namespace panda::test diff --git a/ecmascript/runtime_call_id.h b/ecmascript/runtime_call_id.h new file mode 100644 index 0000000000000000000000000000000000000000..6032ebc626ed9c47747ec1881cc81fe95243ceb5 --- /dev/null +++ b/ecmascript/runtime_call_id.h @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMA_RUNTIME_CALL_ID_H +#define PANDA_RUNTIME_ECMA_RUNTIME_CALL_ID_H + +namespace panda::ecmascript { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTERPRETER_CALLER_LIST(V) \ + V(RunInternal) \ + V(Ldnan) \ + V(Ldinfinity) \ + V(Ldglobalthis) \ + V(Ldundefined) \ + V(Ldboolean) \ + V(Ldnumber) \ + V(Ldstring) \ + V(Ldbigint) \ + V(Ldnull) \ + V(Ldsymbol) \ + V(Ldfunction) \ + V(Ldglobal) \ + V(Ldtrue) \ + V(Ldfalse) \ + V(Tonumber) \ + V(Toboolean) \ + V(Add2Dyn) \ + V(Sub2Dyn) \ + V(Mul2Dyn) \ + V(Div2Dyn) \ + V(Mod2Dyn) \ + V(EqDyn) \ + V(NotEqDyn) \ + V(LessDyn) \ + V(LessEqDyn) \ + V(GreaterDyn) \ + V(GreaterEqDyn) \ + V(StrictNotEqDyn) \ + V(StrictEqDyn) \ + V(Shl2Dyn) \ + V(Shr2Dyn) \ + V(Ashr2Dyn) \ + V(And2Dyn) \ + V(Or2Dyn) \ + V(Xor2Dyn) \ + V(NegDyn) \ + V(NotDyn) \ + V(IncDyn) \ + V(DecDyn) \ + V(ExpDyn) \ + V(ThrowDyn) \ + V(LdObjByIndexDyn) \ + V(StObjByIndexDyn) \ + V(LdObjByNameDyn) \ + V(StObjByNameDyn) \ + V(LdObjByValueDyn) \ + V(StObjByValueDyn) \ + V(StOwnByNameDyn) \ + V(StOwnByIdDyn) \ + V(StOwnByValueDyn) \ + V(Trygetobjprop) \ + V(Delobjprop) \ + V(Defineglobalvar) \ + V(Definelocalvar) \ + V(Definefuncexpr) \ + V(DefinefuncDyn) \ + V(DefineNCFuncDyn) \ + V(NewobjDynrange) \ + V(RefeqDyn) \ + V(TypeofDyn) \ + V(LdnewobjrangeDyn) \ + V(IsinDyn) \ + V(InstanceofDyn) \ + V(NewobjspreadDyn) \ + V(CallArg0Dyn) \ + V(CallArg1Dyn) \ + V(CallArg2Dyn) \ + V(CallArg3Dyn) \ + V(CallThisRangeDyn) \ + V(CallRangeDyn) \ + V(CallSpreadDyn) \ + V(NewlexenvDyn) \ + V(StlexvarDyn) \ + V(LdlexvarDyn) \ + V(LdlexenvDyn) \ + V(GetUnmappedArgs) \ + V(GetPropIterator) \ + V(CreateIterResultObj) \ + V(DefineGeneratorFunc) \ + V(SuspendGenerator) \ + V(ResumeGenerator) \ + V(GetResumeMode) \ + V(CreateGeneratorObj) \ + V(DefineAsyncFunc) \ + V(DefineGetterSetterByValue) \ + V(AsyncFunctionEnter) \ + V(AsyncFunctionAwaitUncaught) \ + V(AsyncFunctionResolveOrReject) \ + V(ThrowUndefined) \ + V(ThrowConstAssignment) \ + V(ThrowUndefinedIfHole) \ + V(Copyrestargs) \ + V(Trystobjprop) \ + V(GetTemplateObject) \ + V(GetIterator) \ + V(ThrowIfNotObject) \ + V(ThrowThrowNotExists) \ + V(CreateObjectWithExcludedKeys) \ + V(ThrowPatternNonCoercible) \ + V(IterNext) \ + V(CloseIterator) \ + V(StArraySpread) \ + V(GetCallSpreadArgs) \ + V(GetIteratorNext) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define BUITINS_API_LIST(V) \ + V(Array, Constructor) \ + V(Array, From) \ + V(Array, Of) \ + V(Array, IsArray) \ + V(Array, Entries) \ + V(Array, Species) \ + V(Array, Concat) \ + V(Array, CopyWithin) \ + V(Array, Fill) \ + V(Array, Filter) \ + V(Array, Find) \ + V(Array, FindIndex) \ + V(Array, IndexOf) \ + V(Array, Join) \ + V(Array, Keys) \ + V(Array, LastIndexOf) \ + V(Array, Map) \ + V(Array, Pop) \ + V(Array, Push) \ + V(Array, Reduce) \ + V(Array, ReduceRight) \ + V(Array, Reverse) \ + V(Array, Shift) \ + V(Array, Slice) \ + V(Array, Some) \ + V(Array, Sort) \ + V(Array, Splice) \ + V(Array, ToLocaleString) \ + V(Array, ToString) \ + V(Array, Unshift) \ + V(Array, Values) \ + V(ArrayBuffer, Constructor) \ + V(ArrayBuffer, Slice) \ + V(ArrayBuffer, GetValueFromBuffer) \ + V(ArrayBuffer, SetValueInBuffer) \ + V(ArrayBuffer, CloneArrayBuffer) \ + V(ArrayBuffer, AllocateArrayBuffer) \ + V(AsyncFunction, Constructor) \ + V(Boolean, Constructor) \ + V(Boolean, ThisBooleanValue) \ + V(DataView, Constructor) \ + V(DataView, GetBuffer) \ + V(DataView, GetByteLength) \ + V(DataView, GetOffset) \ + V(DataView, GetViewValue) \ + V(DataView, SetViewValue) \ + V(Date, Constructor) \ + V(Date, Now) \ + V(Date, UTC) \ + V(Date, Parse) \ + V(Date, GetDateField) \ + V(Date, GetTime) \ + V(Date, SetTime) \ + V(Date, ToJSON) \ + V(Date, ValueOf) \ + V(Date, ToPrimitive) \ + V(Function, Constructor) \ + V(Function, PrototypeApply) \ + V(Function, PrototypeBind) \ + V(Function, PrototypeCall) \ + V(Function, PrototypeToString) \ + V(Function, PrototypeHasInstance) \ + V(Generator, Constructor) \ + V(Generator, PrototypeNext) \ + V(Generator, PrototypeReturn) \ + V(Generator, PrototypeThrow) \ + V(Global, IsFinite) \ + V(Global, IsNaN) \ + V(Global, PrintEntryPoint) \ + V(Global, NewobjDynrange) \ + V(Global, CallJsBoundFunction) \ + V(Global, CallJsProxy) \ + V(Global, DecodeURI) \ + V(Global, EncodeURI) \ + V(Global, DecodeURIComponent) \ + V(Global, EncodeURIComponent) \ + V(Iterator, Constructor) \ + V(Iterator, Next) \ + V(Iterator, Throw) \ + V(Iterator, Return) \ + V(Iterator, GetObj) \ + V(Json, Parse) \ + V(Json, Stringify) \ + V(Map, Constructor) \ + V(Map, Species) \ + V(Map, Clear) \ + V(Map, Delete) \ + V(Map, Entries) \ + V(Map, Get) \ + V(Map, Has) \ + V(Map, Keys) \ + V(Map, Set) \ + V(Map, GetSize) \ + V(Map, Values) \ + V(Math, Abs) \ + V(Math, Acos) \ + V(Math, Acosh) \ + V(Math, Asin) \ + V(Math, Asinh) \ + V(Math, Atan) \ + V(Math, Atanh) \ + V(Math, Atan2) \ + V(Math, Cbrt) \ + V(Math, Ceil) \ + V(Math, Clz32) \ + V(Math, Cos) \ + V(Math, Cosh) \ + V(Math, Exp) \ + V(Math, Expm1) \ + V(Math, Floor) \ + V(Math, Fround) \ + V(Math, Hypot) \ + V(Math, Imul) \ + V(Math, Log) \ + V(Math, Log1p) \ + V(Math, Log10) \ + V(Math, Log2) \ + V(Math, Max) \ + V(Math, Min) \ + V(Math, Pow) \ + V(Math, Random) \ + V(Math, Round) \ + V(Math, Sign) \ + V(Math, Sin) \ + V(Math, Sinh) \ + V(Math, Sqrt) \ + V(Math, Tan) \ + V(Math, Tanh) \ + V(Math, Trunc) \ + V(Number, Constructor) \ + V(Number, IsFinite) \ + V(Number, IsInteger) \ + V(Number, IsNaN) \ + V(Number, IsSafeInteger) \ + V(Number, ParseFloat) \ + V(Number, ParseInt) \ + V(Number, ToExponential) \ + V(Number, ToFixed) \ + V(Number, ToLocaleString) \ + V(Number, ToPrecision) \ + V(Number, ToString) \ + V(Number, ValueOf) \ + V(Number, ThisNumberValue) \ + V(Object, Constructor) \ + V(Object, Assign) \ + V(Object, Create) \ + V(Object, DefineProperties) \ + V(Object, DefineProperty) \ + V(Object, Freeze) \ + V(Object, GetOwnPropertyDesciptor) \ + V(Object, GetOwnPropertyKeys) \ + V(Object, GetOwnPropertyNames) \ + V(Object, GetOwnPropertySymbols) \ + V(Object, GetPrototypeOf) \ + V(Object, Is) \ + V(Object, Keys) \ + V(Object, PreventExtensions) \ + V(Object, Seal) \ + V(Object, SetPrototypeOf) \ + V(Object, HasOwnProperty) \ + V(Object, IsPrototypeOf) \ + V(Object, ToLocaleString) \ + V(Object, GetBuiltinTag) \ + V(Object, ToString) \ + V(Object, ValueOf) \ + V(Object, ProtoGetter) \ + V(Object, ProtoSetter) \ + V(PromiseHandler, Resolve) \ + V(PromiseHandler, Reject) \ + V(PromiseHandler, Executor) \ + V(PromiseHandler, ResolveElementFunction) \ + V(PromiseJob, Reaction) \ + V(PromiseJob, ResolveThenableJob) \ + V(Promise, Constructor) \ + V(Promise, All) \ + V(Promise, Race) \ + V(Promise, Reject) \ + V(Promise, Resolve) \ + V(Promise, GetSpecies) \ + V(Promise, Catch) \ + V(Promise, Then) \ + V(Promise, PerformPromiseThen) \ + V(Proxy, Constructor) \ + V(Proxy, Revocable) \ + V(Proxy, InvalidateProxyFunction) \ + V(Reflect, Apply) \ + V(Reflect, Constructor) \ + V(Reflect, DefineProperty) \ + V(Reflect, DeleteProperty) \ + V(Reflect, Get) \ + V(Reflect, GetOwnPropertyDescriptor) \ + V(Reflect, GetPrototypeOf) \ + V(Reflect, Has) \ + V(Reflect, OwnKeys) \ + V(Reflect, PreventExtensions) \ + V(Reflect, Set) \ + V(Reflect, SetPrototypeOf) \ + V(RegExp, Constructor) \ + V(RegExp, Exec) \ + V(RegExp, Test) \ + V(RegExp, ToString) \ + V(RegExp, GetFlags) \ + V(RegExp, GetSpecies) \ + V(RegExp, Match) \ + V(RegExp, Replace) \ + V(RegExp, Search) \ + V(RegExp, Split) \ + V(RegExp, Create) \ + V(Set, Constructor) \ + V(Set, Species) \ + V(Set, Add) \ + V(Set, Clear) \ + V(Set, Delete) \ + V(Set, Entries) \ + V(Set, Has) \ + V(Set, GetSize) \ + V(Set, Values) \ + V(StringIterator, Next) \ + V(String, Constructor) \ + V(String, FromCharCode) \ + V(String, FromCodePoint) \ + V(String, Raw) \ + V(String, GetSubstitution) \ + V(String, CharAt) \ + V(String, CharCodeAt) \ + V(String, CodePointAt) \ + V(String, Concat) \ + V(String, EndsWith) \ + V(String, Includes) \ + V(String, IndexOf) \ + V(String, LastIndexOf) \ + V(String, LocaleCompare) \ + V(String, Match) \ + V(String, Normalize) \ + V(String, Repeat) \ + V(String, Replace) \ + V(String, Search) \ + V(String, Slice) \ + V(String, Split) \ + V(String, StartsWith) \ + V(String, Substring) \ + V(String, ToLocaleLowerCase) \ + V(String, ToLocaleUpperCase) \ + V(String, ToLowerCase) \ + V(String, ToString) \ + V(String, ToUpperCase) \ + V(String, Trim) \ + V(String, GetStringIterator) \ + V(String, SubStr) \ + V(Symbol, Constructor) \ + V(Symbol, ToString) \ + V(Symbol, ValueOf) \ + V(Symbol, For) \ + V(Symbol, KeyFor) \ + V(Symbol, DescriptionGetter) \ + V(Symbol, ThisSymbolValue) \ + V(Symbol, ToPrimitive) \ + V(Symbol, SymbolDescriptiveString) \ + V(TypedArray, BaseConstructor) \ + V(TypedArray, From) \ + V(TypedArray, Of) \ + V(TypedArray, Species) \ + V(TypedArray, GetBuffer) \ + V(TypedArray, GetByteLength) \ + V(TypedArray, GetByteOffset) \ + V(TypedArray, CopyWithin) \ + V(TypedArray, Entries) \ + V(TypedArray, Every) \ + V(TypedArray, Filter) \ + V(TypedArray, ForEach) \ + V(TypedArray, Keys) \ + V(TypedArray, GetLength) \ + V(TypedArray, Map) \ + V(TypedArray, Set) \ + V(TypedArray, Slice) \ + V(TypedArray, Sort) \ + V(TypedArray, Subarray) \ + V(TypedArray, Values) \ + V(TypedArray, ToStringTag) \ + V(WeakMap, Constructor) \ + V(WeakMap, Delete) \ + V(WeakMap, Get) \ + V(WeakMap, Has) \ + V(WeakMap, Set) \ + V(WeakSet, Constructor) \ + V(WeakSet, Delete) \ + V(WeakSet, Add) \ + V(WeakSet, Has) + +#define ABSTRACT_OPERATION_LIST(V) \ + V(JSTaggedValue, ToString) \ + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTERPRETER_CALLER_ID(name) INTERPRETER_ID_##name, +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define BUILTINS_API_ID(class, name) BUILTINS_ID_##class##_##name, +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GC_RUNPHASE_ID(name) name##_GC_TRACE_RUNPHASE, +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ABSTRACT_OPERATION_ID(class, name) ABSTRACT_ID_##class##_##name, + +enum EcmaRuntimeCallerId { + INTERPRETER_CALLER_LIST(INTERPRETER_CALLER_ID) BUITINS_API_LIST(BUILTINS_API_ID) + ABSTRACT_OPERATION_LIST(ABSTRACT_OPERATION_ID) + GC_INITIALIZE, + RUNTIME_CALLER_NUMBER, +}; + +#ifdef PANDA_ECMASCRIPT_ENABLE_RUNTIME_STAT +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTERPRETER_TRACE(thread, name) \ + [[maybe_unused]] JSThread *_js_thread_ = thread; \ + [[maybe_unused]] EcmaRuntimeStat *_run_stat_ = _js_thread_->GetEcmaVM()->GetRuntimeStat(); \ + RuntimeTimerScope interpret_##name##_scope_(thread, INTERPRETER_CALLER_ID(name) _run_stat_) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define BUILTINS_API_TRACE(thread, class, name) \ + [[maybe_unused]] JSThread *_js_thread_ = thread; \ + [[maybe_unused]] EcmaRuntimeStat *_run_stat_ = _js_thread_->GetEcmaVM()->GetRuntimeStat(); \ + RuntimeTimerScope builtins_##class##name##_scope_(thread, BUILTINS_API_ID(class, name) _run_stat_) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ABSTRACT_OPERATION_TRACE(thread, class, name) \ + [[maybe_unused]] JSThread *_js_thread_ = thread; \ + [[maybe_unused]] EcmaRuntimeStat *_run_stat_ = _js_thread_->GetEcmaVM()->GetRuntimeStat(); \ + RuntimeTimerScope abstract_##class##name##_scope_(thread, ABSTRACT_OPERATION_ID(class, name) _run_stat_) + +#else +#define INTERPRETER_TRACE(thread, name) static_cast(0) // NOLINT(cppcoreguidelines-macro-usage) +#define BUILTINS_API_TRACE(thread, class, name) static_cast(0) // NOLINT(cppcoreguidelines-macro-usage) +#define ABSTRACT_OPERATION_TRACE(thread, class, name) static_cast(0) // NOLINT(cppcoreguidelines-macro-usage) +#endif // PANDA_ECMASCRIPT_ENABLE_RUNTIME_STAT +} // namespace panda::ecmascript +#endif diff --git a/ecmascript/snapshot/mem/BUILD.gn b/ecmascript/snapshot/mem/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..6cc9f6aed508ac28f88d9826005c522642c1c7ea --- /dev/null +++ b/ecmascript/snapshot/mem/BUILD.gn @@ -0,0 +1,54 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//build/ohos.gni") + +snapshot_input_path = "//component_dist/aosp-x86_64/packages_to_install/ace_engine_full/ark_build/strip.native.min.abc" +arkstdlib_abc_path = "//prebuilts/ark_tools/target/aex/arkstdlib.abc" +snapshot_bin_output_path = "$target_gen_dir/snapshot" + +action("gen_snapshot_bin") { + visibility = [ ":*" ] + + deps = [ "//ark/js_runtime/ecmascript/js_vm:ark_js_vm(${host_toolchain})" ] + + script = "$ark_root/tools/gen_snapshot.sh" + + args = [ + "./clang_x64/ark/ark_js_runtime/ark_js_vm ", + "--boot-class-spaces=core:ecmascript", + "--boot-intrinsic-spaces=core:ecmascript", + "--compiler-enable-jit=false", + "--gc-type=epsilon", + "--runtime-type=ecmascript", + "--snapshot-serialize-enabled=true", + "--snapshot-file=" + rebase_path(snapshot_bin_output_path), + rebase_path(snapshot_input_path), + "_GLOBAL::func_main_0", + ] + + inputs = [ + snapshot_input_path, + arkstdlib_abc_path, + ] + outputs = [ snapshot_bin_output_path ] +} + +ohos_prebuilt_etc("gen_ark_snapshot_bin") { + deps = [ ":gen_snapshot_bin" ] + source = snapshot_bin_output_path + + install_enable = true + subsystem_name = "ark" +} diff --git a/ecmascript/snapshot/mem/constants.h b/ecmascript/snapshot/mem/constants.h new file mode 100644 index 0000000000000000000000000000000000000000..943482bd3ab7c3386e569e6c83e20d31e410f7ff --- /dev/null +++ b/ecmascript/snapshot/mem/constants.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_ECMASCRIPT_MEM_CONSTANTS_H +#define PANDA_ECMASCRIPT_MEM_CONSTANTS_H + +// slot bit +static constexpr int OBJECT_INDEX_BIT_NUMBER = 13; // object index +static constexpr int OBJECT_TO_STRING_FLAG_NUMBER = 1; // 1 : reference to string +static constexpr int OBJECT_IN_CONSTANTS_INDEX_NUMBER = 8; // index +static constexpr int OBJECT_TYPE_BIT_NUMBER = 7; // js_type +static constexpr int OBJECT_SIZE_BIT_NUMBER = 18; // 4M +static constexpr int OBJECT_SPECIAL = 1; // special +static constexpr int IS_REFERENCE_SLOT_BIT_NUMBER = 16; // [0x0000] is reference + +static constexpr int MAX_C_POINTER_INDEX = 1024 * 8 - 1; +static constexpr int MAX_OBJECT_INDEX = 8192; +static constexpr int MAX_OBJECT_SIZE_INDEX = 1024 * 256 - 1; + +// object or space align up +static constexpr size_t PAGE_SIZE_ALIGN_UP = 4096; +static constexpr size_t MAX_UINT_32 = 0xFFFFFFFF; + +// builtins native method encode +// builtins deserialize: nativeMehtods_ + getter/setter; program deserialize: getter/setter + programFunctionMethods +static constexpr size_t PROGRAM_NATIVE_METHOD_BEGIN = 6; + +// address slot size +static constexpr int ADDRESS_SIZE = sizeof(uintptr_t); + +// serialize use constants +static constexpr uint8_t MASK_METHOD_SPACE_BEGIN = 0x7F; +static constexpr int OBJECT_SIZE_EXTEND_PAGE = 512; +static constexpr int NATIVE_METHOD_SIZE = 427; + +static constexpr size_t MANAGED_OBJECT_OFFSET = 16; + +#endif // PANDA_ECMASCRIPT_MEM_CONSTANTS_H diff --git a/ecmascript/snapshot/mem/slot_bit.cpp b/ecmascript/snapshot/mem/slot_bit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..720aee6a43a55b7766c13e9cbd9f2e5f1840b767 --- /dev/null +++ b/ecmascript/snapshot/mem/slot_bit.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/snapshot/mem/slot_bit.h" + +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +/* static */ +uint8_t SerializeHelper::GetObjectType(TaggedObject *objectHeader) +{ + auto hclass = objectHeader->GetClass(); + return static_cast(JSHClass::Cast(hclass)->GetObjectType()); +} + +/* static */ +SlotBit SerializeHelper::AddObjectHeaderToData(TaggedObject *objectHeader, CQueue *queue, + std::unordered_map *data, size_t index) +{ + queue->emplace(objectHeader); + SlotBit slotBits(data->size()); + slotBits.SetObjectInConstantsIndex(index); + data->emplace(ToUintPtr(objectHeader), slotBits); + return slotBits; +} + +void SerializeHelper::AddTaggedObjectRangeToData(ObjectSlot start, ObjectSlot end, CQueue *queue, + std::unordered_map *data) +{ + size_t index = 0; + while (start < end) { + JSTaggedValue object(start.GetTaggedType()); + index++; + start++; + if (object.IsHeapObject()) { + SlotBit slotBit(0); + if (data->find(object.GetRawData()) == data->end()) { + slotBit = AddObjectHeaderToData(object.GetTaggedObject(), queue, data, index); + } + } + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/snapshot/mem/slot_bit.h b/ecmascript/snapshot/mem/slot_bit.h new file mode 100644 index 0000000000000000000000000000000000000000..a9c79edf5d006e1f9f0e8d049338fcf90be46390 --- /dev/null +++ b/ecmascript/snapshot/mem/slot_bit.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_ECMASCRIPT_MEM_SLOT_BIT_H +#define PANDA_ECMASCRIPT_MEM_SLOT_BIT_H + +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/mem/slots.h" +#include "ecmascript/snapshot/mem/constants.h" + +#include "utils/bit_field.h" + +namespace panda::ecmascript { +class SlotBit final { +public: + ~SlotBit() = default; + explicit SlotBit() = delete; + + DEFAULT_COPY_SEMANTIC(SlotBit); + DEFAULT_MOVE_SEMANTIC(SlotBit); + + explicit SlotBit(uint64_t value) : value_(value) {} + + using ObjectIndexBits = BitField; + using ObjectToStringBits = ObjectIndexBits::NextField; + using ObjectInConstantsIndexBits = ObjectToStringBits::NextField; + using ObjectTypeBits = ObjectInConstantsIndexBits::NextField; + using ObjectSizeBits = ObjectTypeBits::NextField; + using ObjectSpecial = ObjectSizeBits::NextField; + using IsReferenceSlotBits = ObjectSpecial::NextField; + + void SetReferenceToString(bool flag) + { + ObjectToStringBits::Set(flag, &value_); + } + + void SetObjectInConstantsIndex(size_t index) + { + ObjectInConstantsIndexBits::Set(index, &value_); + } + + size_t GetObjectInConstantsIndex() const + { + return ObjectInConstantsIndexBits::Decode(value_); + } + + bool IsReferenceToString() const + { + return ObjectToStringBits::Decode(value_); + } + + void SetObjectType(uint8_t object_type) + { + ObjectTypeBits::Set(object_type, &value_); + } + + void SetObjectSize(size_t object_size) + { + ObjectSizeBits::Set(object_size, &value_); + } + + void SetObjectIndex(uint32_t object_index) + { + ObjectIndexBits::Set(object_index, &value_); + } + + uint64_t GetValue() const + { + return value_; + } + + uint32_t GetObjectIndex() const + { + return ObjectIndexBits::Decode(value_); + } + + uint8_t GetObjectType() const + { + return ObjectTypeBits::Decode(value_); + } + + size_t GetObjectSize() const + { + return ObjectSizeBits::Decode(value_); + } + + bool IsReferenceSlot() const + { + return IsReferenceSlotBits::Decode(value_) == 0; + } + + bool IsSpecial() const + { + return ObjectSpecial::Decode(value_); + } + + void SetObjectSpecial() + { + ObjectSpecial::Set(true, &value_); + } + + void ClearObjectSpecialFlag() + { + ObjectSpecial::Set(false, &value_); + } + +private: + uint64_t value_; +}; + +class SerializeHelper final { +public: + static uint8_t GetObjectType(TaggedObject *objectHeader); + + static SlotBit AddObjectHeaderToData(TaggedObject *objectHeader, CQueue *queue, + std::unordered_map *data, size_t index = 0); + static void AddTaggedObjectRangeToData(ObjectSlot start, ObjectSlot end, CQueue *queue, + std::unordered_map *data); +}; +} // namespace panda::ecmascript + +#endif // PANDA_ECMASCRIPT_MEM_SLOT_BIT_H diff --git a/ecmascript/snapshot/mem/snapshot.cpp b/ecmascript/snapshot/mem/snapshot.cpp new file mode 100644 index 0000000000000000000000000000000000000000..978c19b69c37eb4a4b0d3a5215588439ef920729 --- /dev/null +++ b/ecmascript/snapshot/mem/snapshot.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/snapshot/mem/snapshot.h" + +#include +#include +#include +#include + +#include "ecmascript/class_linker/program_object.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/jobs/micro_job_queue.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/mem/ecma_heap_manager.h" +#include "ecmascript/mem/heap.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/snapshot/mem/snapshot_serialize.h" +#include "libpandabase/mem/mem.h" + +namespace panda::ecmascript { +constexpr uint32_t PANDA_FILE_ALIGNMENT = 4096; + +void SnapShot::MakeSnapShotProgramObject(Program *program, const panda_file::File *pf, const CString &fileName) +{ + std::fstream write; + std::pair filePath = VerifyFilePath(fileName); + if (!filePath.first) { + LOG(ERROR, RUNTIME) << "snapshot file path error"; + return; + } + write.open(filePath.second.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + if (!write.good()) { + LOG(DEBUG, RUNTIME) << "snapshot open file failed"; + return; + } + + SnapShotSerialize serialize(vm_, true); + + std::unordered_map data; + CQueue objectQueue; + + serialize.RegisterNativeMethod(); + + // handle GlobalEnvConstants + auto constant = const_cast(vm_->GetJSThread()->GlobalConstants()); + constant->Visitor([&objectQueue, &data](Root type, ObjectSlot start, ObjectSlot end) { + SerializeHelper::AddTaggedObjectRangeToData(start, end, &objectQueue, &data); + }); + + vm_->Iterate([&objectQueue, &data](Root type, ObjectSlot object) { + SerializeHelper::AddObjectHeaderToData(object.GetTaggedObjectHeader(), &objectQueue, &data); + }); + + while (!objectQueue.empty()) { + auto taggedObject = objectQueue.front(); + if (taggedObject == nullptr) { + break; + } + objectQueue.pop(); + + serialize.Serialize(taggedObject, &objectQueue, &data); + } + + serialize.SetProgramSerializeStart(); + + // handle program + if (program != nullptr) { + SerializeHelper::AddObjectHeaderToData(program, &objectQueue, &data); + } + + while (!objectQueue.empty()) { + auto taggedObject = objectQueue.front(); + if (taggedObject == nullptr) { + break; + } + objectQueue.pop(); + serialize.Serialize(taggedObject, &objectQueue, &data); + } + + serialize.SerializePandaFileMethod(); + + auto region = vm_->GetHeap()->GetSnapShotSpace()->GetCurrentRegion(); + region->SetHighWaterMark(vm_->GetFactory()->GetHeapManager().GetSnapShotSpaceAllocator().GetTop()); + + // write to file + SnapShotSpace *space = vm_->GetHeap()->GetSnapShotSpace(); + uint32_t snapshot_size = space->GetRegionCount() * DEFAULT_SNAPSHOT_SPACE_SIZE; + uint32_t panda_file_begin = RoundUp(snapshot_size + sizeof(Header), PANDA_FILE_ALIGNMENT); + Header hdr {snapshot_size, panda_file_begin}; + write.write(reinterpret_cast(&hdr), sizeof(hdr)); + + space->EnumerateRegions([&write](Region *current) { + write.write(reinterpret_cast(current), DEFAULT_SNAPSHOT_SPACE_SIZE); + write.flush(); + }); + space->ReclaimRegions(); + ASSERT(static_cast(write.tellp()) == snapshot_size + sizeof(Header)); + + write.seekp(panda_file_begin); + write.write(reinterpret_cast(pf->GetBase()), pf->GetHeader()->file_size); + write.close(); +} + +std::unique_ptr SnapShot::DeserializeGlobalEnvAndProgram(const CString &fileName) +{ + SnapShotSerialize serialize(vm_, false); + + serialize.GeneratedNativeMethod(); + + std::pair filePath = VerifyFilePath(fileName); + if (!filePath.first) { + LOG(ERROR, RUNTIME) << "snapshot file path error"; + return std::unique_ptr(); + } + + int fd = open(filePath.second.c_str(), O_CLOEXEC); // NOLINT(cppcoreguidelines-pro-type-vararg) + if (UNLIKELY(fd == -1)) { + LOG_ECMA(FATAL) << "open file failed"; + UNREACHABLE(); + } + size_t file_size = lseek(fd, 0, SEEK_END); + auto readFile = ToUintPtr(mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0)); + auto hdr = *ToNativePtr(readFile); + if (hdr.snapshot_size % DEFAULT_SNAPSHOT_SPACE_SIZE != 0) { + LOG_ECMA(FATAL) << "Invalid snapshot file"; + UNREACHABLE(); + } + SnapShotSpace *space = vm_->GetHeap()->GetSnapShotSpace(); + + uintptr_t snapshot_begin = readFile + sizeof(Header); + for (size_t i = 0; i < hdr.snapshot_size / DEFAULT_SNAPSHOT_SPACE_SIZE; i++) { + Region *region = vm_->GetHeap()->GetRegionFactory()->AllocateAlignedRegion(space, DEFAULT_SNAPSHOT_SPACE_SIZE); + auto fileRegion = ToNativePtr(snapshot_begin + i * DEFAULT_SNAPSHOT_SPACE_SIZE); + + uint64_t base = region->allocateBase_; + uint64_t begin = (fileRegion->begin_) % DEFAULT_SNAPSHOT_SPACE_SIZE; + uint64_t waterMark = (fileRegion->highWaterMark_) % DEFAULT_SNAPSHOT_SPACE_SIZE; + + if (memcpy_s(region, DEFAULT_SNAPSHOT_SPACE_SIZE, fileRegion, DEFAULT_SNAPSHOT_SPACE_SIZE) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + + // space + region->space_ = space; + // allocate_base_ + region->allocateBase_ = base; + // begin_ + region->begin_ = ToUintPtr(region) + begin; + // end_ + region->end_ = ToUintPtr(region) + DEFAULT_SNAPSHOT_SPACE_SIZE; + // high_water_mark_ + region->highWaterMark_ = ToUintPtr(region) + waterMark; + // prev_ + region->prev_ = nullptr; + // next_ + region->next_ = nullptr; + // mark_bitmap_ + region->markBitmap_ = nullptr; + // cross_region_set_ + region->crossRegionSet_ = nullptr; + // old_to_new_set_ + region->oldToNewSet_ = nullptr; + + space->AddRegion(region); + } + munmap(ToNativePtr(readFile), hdr.panda_file_begin); + uintptr_t panda_file_mem = readFile + hdr.panda_file_begin; + auto pf = + panda_file::File::OpenFromMemory(os::mem::ConstBytePtr(ToNativePtr(panda_file_mem), + file_size - hdr.panda_file_begin, os::mem::MmapDeleter), + fileName); + close(fd); + // redirect object field + serialize.RedirectSlot(pf.get()); + return pf; +} + +size_t SnapShot::AlignUpPageSize(size_t spaceSize) +{ + if (spaceSize % PAGE_SIZE_ALIGN_UP == 0) { + return spaceSize; + } + return PAGE_SIZE_ALIGN_UP * (spaceSize / PAGE_SIZE_ALIGN_UP + 1); +} + +std::pair SnapShot::VerifyFilePath(const CString &filePath) +{ + if (filePath.size() > PATH_MAX) { + return std::make_pair(false, ""); + } + CVector resolvedPath(PATH_MAX); + auto result = realpath(filePath.c_str(), resolvedPath.data()); + if (result == resolvedPath.data() || errno == ENOENT) { + return std::make_pair(true, CString(resolvedPath.data())); + } + return std::make_pair(false, ""); +} +} // namespace panda::ecmascript diff --git a/ecmascript/snapshot/mem/snapshot.h b/ecmascript/snapshot/mem/snapshot.h new file mode 100644 index 0000000000000000000000000000000000000000..e8fcbbe6b80d7bed2caa2fbeea0d19d1c17c3652 --- /dev/null +++ b/ecmascript/snapshot/mem/snapshot.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_ECMASCRIPT_MEM_SNAPSHOT_H +#define PANDA_ECMASCRIPT_MEM_SNAPSHOT_H + +#include "libpandabase/macros.h" +#include "libpandafile/file.h" + +#include "ecmascript/snapshot/mem/slot_bit.h" +#include "ecmascript/mem/c_string.h" + +namespace panda::ecmascript { +class Program; +class EcmaVM; + +class SnapShot final { +public: + explicit SnapShot(EcmaVM *vm) : vm_(vm) {} + ~SnapShot() = default; + + void MakeSnapShotProgramObject(Program *program, const panda_file::File *pf, + const CString &fileName = "./snapshot"); + std::unique_ptr DeserializeGlobalEnvAndProgram( + const CString &fileName = "./snapshot"); + +private: + struct Header { + uint32_t snapshot_size; + uint32_t panda_file_begin; + }; + +private: + size_t AlignUpPageSize(size_t spaceSize); + std::pair VerifyFilePath(const CString &filePath); + + NO_MOVE_SEMANTIC(SnapShot); + NO_COPY_SEMANTIC(SnapShot); + + EcmaVM *vm_; +}; +} // namespace panda::ecmascript +#endif // PANDA_ECMASCRIPT_MEM_SNAPSHOT_H \ No newline at end of file diff --git a/ecmascript/snapshot/mem/snapshot_serialize.cpp b/ecmascript/snapshot/mem/snapshot_serialize.cpp new file mode 100644 index 0000000000000000000000000000000000000000..125885ca2bf99e814fe6a19596b5745bc5147145 --- /dev/null +++ b/ecmascript/snapshot/mem/snapshot_serialize.cpp @@ -0,0 +1,1174 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/snapshot/mem/snapshot_serialize.h" + +#include "ecmascript/base/error_type.h" +#include "ecmascript/builtins/builtins_array.h" +#include "ecmascript/builtins/builtins_arraybuffer.h" +#include "ecmascript/builtins/builtins_async_function.h" +#include "ecmascript/builtins/builtins_boolean.h" +#include "ecmascript/builtins/builtins_dataview.h" +#include "ecmascript/builtins/builtins_date.h" +#include "ecmascript/builtins/builtins_errors.h" +#include "ecmascript/builtins/builtins_function.h" +#include "ecmascript/builtins/builtins_generator.h" +#include "ecmascript/builtins/builtins_global.h" +#include "ecmascript/builtins/builtins_iterator.h" +#include "ecmascript/builtins/builtins_json.h" +#include "ecmascript/builtins/builtins_map.h" +#include "ecmascript/builtins/builtins_math.h" +#include "ecmascript/builtins/builtins_number.h" +#include "ecmascript/builtins/builtins_object.h" +#include "ecmascript/builtins/builtins_promise.h" +#include "ecmascript/builtins/builtins_promise_handler.h" +#include "ecmascript/builtins/builtins_promise_job.h" +#include "ecmascript/builtins/builtins_proxy.h" +#include "ecmascript/builtins/builtins_reflect.h" +#include "ecmascript/builtins/builtins_regexp.h" +#include "ecmascript/builtins/builtins_set.h" +#include "ecmascript/builtins/builtins_string.h" +#include "ecmascript/builtins/builtins_string_iterator.h" +#include "ecmascript/builtins/builtins_symbol.h" +#include "ecmascript/builtins/builtins_typedarray.h" +#include "ecmascript/builtins/builtins_weak_map.h" +#include "ecmascript/builtins/builtins_weak_set.h" +#include "ecmascript/class_linker/program_object.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_for_in_iterator.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_map_iterator.h" +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/region_factory.h" +#include "ecmascript/mem/space.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +using Number = builtins::BuiltinsNumber; +using Object = builtins::BuiltinsObject; +using Date = builtins::BuiltinsDate; +using Symbol = builtins::BuiltinsSymbol; +using Boolean = builtins::BuiltinsBoolean; +using BuiltinsMap = builtins::BuiltinsMap; +using BuiltinsSet = builtins::BuiltinsSet; +using BuiltinsWeakMap = builtins::BuiltinsWeakMap; +using BuiltinsWeakSet = builtins::BuiltinsWeakSet; +using BuiltinsArray = builtins::BuiltinsArray; +using BuiltinsTypedArray = builtins::BuiltinsTypedArray; +using BuiltinsIterator = builtins::BuiltinsIterator; +using Error = builtins::BuiltinsError; +using RangeError = builtins::BuiltinsRangeError; +using ReferenceError = builtins::BuiltinsReferenceError; +using TypeError = builtins::BuiltinsTypeError; +using URIError = builtins::BuiltinsURIError; +using SyntaxError = builtins::BuiltinsSyntaxError; +using EvalError = builtins::BuiltinsEvalError; +using ErrorType = base::ErrorType; +using Global = builtins::BuiltinsGlobal; +using BuiltinsString = builtins::BuiltinsString; +using StringIterator = builtins::BuiltinsStringIterator; +using RegExp = builtins::BuiltinsRegExp; +using Function = builtins::BuiltinsFunction; +using Math = builtins::BuiltinsMath; +using ArrayBuffer = builtins::BuiltinsArrayBuffer; +using Json = builtins::BuiltinsJson; +using Proxy = builtins::BuiltinsProxy; +using Reflect = builtins::BuiltinsReflect; +using AsyncFunction = builtins::BuiltinsAsyncFunction; +using GeneratorObject = builtins::BuiltinsGenerator; +using Promise = builtins::BuiltinsPromise; +using BuiltinsPromiseHandler = builtins::BuiltinsPromiseHandler; +using BuiltinsPromiseJob = builtins::BuiltinsPromiseJob; +using ErrorType = base::ErrorType; +using DataView = builtins::BuiltinsDataView; + +constexpr int TAGGED_SIZE = JSTaggedValue::TaggedTypeSize(); +constexpr int OBJECT_HEADER_SIZE = TaggedObject::ObjectHeaderSize(); +constexpr int METHOD_SIZE = sizeof(JSMethod); + +// NOLINTNEXTLINE(modernize-avoid-c-arrays) +static uintptr_t g_nativeTable[] = { + reinterpret_cast(nullptr), + reinterpret_cast(BuiltinsMap::Species), + reinterpret_cast(StringIterator::Next), + reinterpret_cast(Function::FunctionPrototypeInvokeSelf), + reinterpret_cast(Function::FunctionConstructor), + reinterpret_cast(JSFunction::AccessCallerArgumentsThrowTypeError), + reinterpret_cast(Function::FunctionPrototypeApply), + reinterpret_cast(Function::FunctionPrototypeBind), + reinterpret_cast(Function::FunctionPrototypeCall), + reinterpret_cast(Function::FunctionPrototypeToString), + reinterpret_cast(Object::ObjectConstructor), + reinterpret_cast(Error::ErrorConstructor), + reinterpret_cast(Error::ToString), + reinterpret_cast(RangeError::RangeErrorConstructor), + reinterpret_cast(RangeError::ToString), + reinterpret_cast(ReferenceError::ReferenceErrorConstructor), + reinterpret_cast(ReferenceError::ToString), + reinterpret_cast(TypeError::TypeErrorConstructor), + reinterpret_cast(TypeError::ToString), + reinterpret_cast(TypeError::ThrowTypeError), + reinterpret_cast(URIError::URIErrorConstructor), + reinterpret_cast(URIError::ToString), + reinterpret_cast(SyntaxError::SyntaxErrorConstructor), + reinterpret_cast(SyntaxError::ToString), + reinterpret_cast(EvalError::EvalErrorConstructor), + reinterpret_cast(EvalError::ToString), + reinterpret_cast(Number::NumberConstructor), + reinterpret_cast(Number::ToExponential), + reinterpret_cast(Number::ToFixed), + reinterpret_cast(Number::ToPrecision), + reinterpret_cast(Number::ToString), + reinterpret_cast(Number::ValueOf), + reinterpret_cast(Number::IsFinite), + reinterpret_cast(Number::IsInteger), + reinterpret_cast(Number::IsNaN), + reinterpret_cast(Number::IsSafeInteger), + reinterpret_cast(Number::ParseFloat), + reinterpret_cast(Number::ParseInt), + reinterpret_cast(Symbol::SymbolConstructor), + reinterpret_cast(Symbol::For), + reinterpret_cast(Symbol::KeyFor), + reinterpret_cast(Symbol::DescriptionGetter), + reinterpret_cast(Symbol::ToPrimitive), + reinterpret_cast(Symbol::ToString), + reinterpret_cast(Symbol::ValueOf), + reinterpret_cast(Function::FunctionPrototypeHasInstance), + reinterpret_cast(Date::DateConstructor), + reinterpret_cast(Date::GetDate), + reinterpret_cast(Date::GetDay), + reinterpret_cast(Date::GetFullYear), + reinterpret_cast(Date::GetHours), + reinterpret_cast(Date::GetMilliseconds), + reinterpret_cast(Date::GetMinutes), + reinterpret_cast(Date::GetMonth), + reinterpret_cast(Date::GetSeconds), + reinterpret_cast(Date::GetTime), + reinterpret_cast(Date::GetTimezoneOffset), + reinterpret_cast(Date::GetUTCDate), + reinterpret_cast(Date::GetUTCDay), + reinterpret_cast(Date::GetUTCFullYear), + reinterpret_cast(Date::GetUTCHours), + reinterpret_cast(Date::GetUTCMilliseconds), + reinterpret_cast(Date::GetUTCMinutes), + reinterpret_cast(Date::GetUTCMonth), + reinterpret_cast(Date::GetUTCSeconds), + reinterpret_cast(Date::SetDate), + reinterpret_cast(Date::SetFullYear), + reinterpret_cast(Date::SetHours), + reinterpret_cast(Date::SetMilliseconds), + reinterpret_cast(Date::SetMinutes), + reinterpret_cast(Date::SetMonth), + reinterpret_cast(Date::SetSeconds), + reinterpret_cast(Date::SetTime), + reinterpret_cast(Date::SetUTCDate), + reinterpret_cast(Date::SetUTCFullYear), + reinterpret_cast(Date::SetUTCHours), + reinterpret_cast(Date::SetUTCMilliseconds), + reinterpret_cast(Date::SetUTCMinutes), + reinterpret_cast(Date::SetUTCMonth), + reinterpret_cast(Date::SetUTCSeconds), + reinterpret_cast(Date::ToDateString), + reinterpret_cast(Date::ToISOString), + reinterpret_cast(Date::ToJSON), + reinterpret_cast(Date::ToString), + reinterpret_cast(Date::ToTimeString), + reinterpret_cast(Date::ToUTCString), + reinterpret_cast(Date::ValueOf), + reinterpret_cast(Date::ToPrimitive), + reinterpret_cast(Date::Now), + reinterpret_cast(Date::Parse), + reinterpret_cast(Date::UTC), + reinterpret_cast(Object::Assign), + reinterpret_cast(Object::Create), + reinterpret_cast(Object::DefineProperties), + reinterpret_cast(Object::DefineProperty), + reinterpret_cast(Object::Freeze), + reinterpret_cast(Object::GetOwnPropertyDesciptor), + reinterpret_cast(Object::GetOwnPropertyNames), + reinterpret_cast(Object::GetOwnPropertySymbols), + reinterpret_cast(Object::GetPrototypeOf), + reinterpret_cast(Object::Is), + reinterpret_cast(Object::IsExtensible), + reinterpret_cast(Object::IsFrozen), + reinterpret_cast(Object::IsSealed), + reinterpret_cast(Object::Keys), + reinterpret_cast(Object::PreventExtensions), + reinterpret_cast(Object::Seal), + reinterpret_cast(Object::SetPrototypeOf), + reinterpret_cast(Object::HasOwnProperty), + reinterpret_cast(Object::IsPrototypeOf), + reinterpret_cast(Object::PropertyIsEnumerable), + reinterpret_cast(Object::ToLocaleString), + reinterpret_cast(Object::ToString), + reinterpret_cast(Object::ValueOf), + reinterpret_cast(Object::ProtoGetter), + reinterpret_cast(Object::ProtoSetter), + reinterpret_cast(Object::CreateRealm), + reinterpret_cast(Object::Entries), + reinterpret_cast(Boolean::BooleanConstructor), + reinterpret_cast(Boolean::BooleanPrototypeToString), + reinterpret_cast(Boolean::BooleanPrototypeValueOf), + reinterpret_cast(RegExp::RegExpConstructor), + reinterpret_cast(RegExp::Exec), + reinterpret_cast(RegExp::Test), + reinterpret_cast(RegExp::ToString), + reinterpret_cast(RegExp::GetFlags), + reinterpret_cast(RegExp::GetSource), + reinterpret_cast(RegExp::GetGlobal), + reinterpret_cast(RegExp::GetIgnoreCase), + reinterpret_cast(RegExp::GetMultiline), + reinterpret_cast(RegExp::GetDotAll), + reinterpret_cast(RegExp::GetSticky), + reinterpret_cast(RegExp::GetUnicode), + reinterpret_cast(RegExp::Split), + reinterpret_cast(RegExp::Search), + reinterpret_cast(RegExp::Match), + reinterpret_cast(RegExp::Replace), + reinterpret_cast(BuiltinsSet::SetConstructor), + reinterpret_cast(BuiltinsSet::Add), + reinterpret_cast(BuiltinsSet::Clear), + reinterpret_cast(BuiltinsSet::Delete), + reinterpret_cast(BuiltinsSet::Has), + reinterpret_cast(BuiltinsSet::ForEach), + reinterpret_cast(BuiltinsSet::Entries), + reinterpret_cast(BuiltinsSet::Values), + reinterpret_cast(BuiltinsSet::GetSize), + reinterpret_cast(BuiltinsSet::Species), + reinterpret_cast(BuiltinsMap::MapConstructor), + reinterpret_cast(BuiltinsMap::Set), + reinterpret_cast(BuiltinsMap::Clear), + reinterpret_cast(BuiltinsMap::Delete), + reinterpret_cast(BuiltinsMap::Has), + reinterpret_cast(BuiltinsMap::Get), + reinterpret_cast(BuiltinsMap::ForEach), + reinterpret_cast(BuiltinsMap::Keys), + reinterpret_cast(BuiltinsMap::Values), + reinterpret_cast(BuiltinsMap::Entries), + reinterpret_cast(BuiltinsMap::GetSize), + reinterpret_cast(BuiltinsWeakMap::WeakMapConstructor), + reinterpret_cast(BuiltinsWeakMap::Set), + reinterpret_cast(BuiltinsWeakMap::Delete), + reinterpret_cast(BuiltinsWeakMap::Has), + reinterpret_cast(BuiltinsWeakMap::Get), + reinterpret_cast(BuiltinsWeakSet::WeakSetConstructor), + reinterpret_cast(BuiltinsWeakSet::Add), + reinterpret_cast(BuiltinsWeakSet::Delete), + reinterpret_cast(BuiltinsWeakSet::Has), + reinterpret_cast(BuiltinsArray::ArrayConstructor), + reinterpret_cast(BuiltinsArray::Concat), + reinterpret_cast(BuiltinsArray::CopyWithin), + reinterpret_cast(BuiltinsArray::Entries), + reinterpret_cast(BuiltinsArray::Every), + reinterpret_cast(BuiltinsArray::Fill), + reinterpret_cast(BuiltinsArray::Filter), + reinterpret_cast(BuiltinsArray::Find), + reinterpret_cast(BuiltinsArray::FindIndex), + reinterpret_cast(BuiltinsArray::ForEach), + reinterpret_cast(BuiltinsArray::IndexOf), + reinterpret_cast(BuiltinsArray::Join), + reinterpret_cast(BuiltinsArray::Keys), + reinterpret_cast(BuiltinsArray::LastIndexOf), + reinterpret_cast(BuiltinsArray::Map), + reinterpret_cast(BuiltinsArray::Pop), + reinterpret_cast(BuiltinsArray::Push), + reinterpret_cast(BuiltinsArray::Reduce), + reinterpret_cast(BuiltinsArray::ReduceRight), + reinterpret_cast(BuiltinsArray::Reverse), + reinterpret_cast(BuiltinsArray::Shift), + reinterpret_cast(BuiltinsArray::Slice), + reinterpret_cast(BuiltinsArray::Some), + reinterpret_cast(BuiltinsArray::Sort), + reinterpret_cast(BuiltinsArray::Splice), + reinterpret_cast(BuiltinsArray::ToLocaleString), + reinterpret_cast(BuiltinsArray::ToString), + reinterpret_cast(BuiltinsArray::Unshift), + reinterpret_cast(BuiltinsArray::Values), + reinterpret_cast(BuiltinsArray::From), + reinterpret_cast(BuiltinsArray::IsArray), + reinterpret_cast(BuiltinsArray::Of), + reinterpret_cast(BuiltinsArray::Species), + reinterpret_cast(BuiltinsArray::Unscopables), + reinterpret_cast(BuiltinsTypedArray::TypedArrayBaseConstructor), + reinterpret_cast(BuiltinsTypedArray::CopyWithin), + reinterpret_cast(BuiltinsTypedArray::Entries), + reinterpret_cast(BuiltinsTypedArray::Every), + reinterpret_cast(BuiltinsTypedArray::Fill), + reinterpret_cast(BuiltinsTypedArray::Filter), + reinterpret_cast(BuiltinsTypedArray::Find), + reinterpret_cast(BuiltinsTypedArray::FindIndex), + reinterpret_cast(BuiltinsTypedArray::ForEach), + reinterpret_cast(BuiltinsTypedArray::IndexOf), + reinterpret_cast(BuiltinsTypedArray::Join), + reinterpret_cast(BuiltinsTypedArray::Keys), + reinterpret_cast(BuiltinsTypedArray::LastIndexOf), + reinterpret_cast(BuiltinsTypedArray::Map), + reinterpret_cast(BuiltinsTypedArray::Reduce), + reinterpret_cast(BuiltinsTypedArray::ReduceRight), + reinterpret_cast(BuiltinsTypedArray::Reverse), + reinterpret_cast(BuiltinsTypedArray::Set), + reinterpret_cast(BuiltinsTypedArray::Slice), + reinterpret_cast(BuiltinsTypedArray::Some), + reinterpret_cast(BuiltinsTypedArray::Sort), + reinterpret_cast(BuiltinsTypedArray::Subarray), + reinterpret_cast(BuiltinsTypedArray::ToLocaleString), + reinterpret_cast(BuiltinsTypedArray::Values), + reinterpret_cast(BuiltinsTypedArray::GetBuffer), + reinterpret_cast(BuiltinsTypedArray::GetByteLength), + reinterpret_cast(BuiltinsTypedArray::GetByteOffset), + reinterpret_cast(BuiltinsTypedArray::GetLength), + reinterpret_cast(BuiltinsTypedArray::ToStringTag), + reinterpret_cast(BuiltinsTypedArray::From), + reinterpret_cast(BuiltinsTypedArray::Of), + reinterpret_cast(BuiltinsTypedArray::Species), + reinterpret_cast(BuiltinsTypedArray::Int8ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Uint8ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Uint8ClampedArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Int16ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Uint16ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Int32ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Uint32ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Float32ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Float64ArrayConstructor), + reinterpret_cast(BuiltinsString::StringConstructor), + reinterpret_cast(BuiltinsString::CharAt), + reinterpret_cast(BuiltinsString::CharCodeAt), + reinterpret_cast(BuiltinsString::CodePointAt), + reinterpret_cast(BuiltinsString::Concat), + reinterpret_cast(BuiltinsString::EndsWith), + reinterpret_cast(BuiltinsString::Includes), + reinterpret_cast(BuiltinsString::IndexOf), + reinterpret_cast(BuiltinsString::LastIndexOf), + reinterpret_cast(BuiltinsString::LocaleCompare), + reinterpret_cast(BuiltinsString::Match), + reinterpret_cast(BuiltinsString::Repeat), + reinterpret_cast(BuiltinsString::Replace), + reinterpret_cast(BuiltinsString::Search), + reinterpret_cast(BuiltinsString::Slice), + reinterpret_cast(BuiltinsString::Split), + reinterpret_cast(BuiltinsString::StartsWith), + reinterpret_cast(BuiltinsString::Substring), + reinterpret_cast(BuiltinsString::SubStr), + reinterpret_cast(BuiltinsString::ToLowerCase), + reinterpret_cast(BuiltinsString::ToString), + reinterpret_cast(BuiltinsString::ToUpperCase), + reinterpret_cast(BuiltinsString::Trim), + reinterpret_cast(BuiltinsString::ValueOf), + reinterpret_cast(BuiltinsString::GetStringIterator), + reinterpret_cast(BuiltinsString::FromCharCode), + reinterpret_cast(BuiltinsString::FromCodePoint), + reinterpret_cast(BuiltinsString::Raw), + reinterpret_cast(BuiltinsString::GetLength), + reinterpret_cast(ArrayBuffer::ArrayBufferConstructor), + reinterpret_cast(ArrayBuffer::Slice), + reinterpret_cast(ArrayBuffer::IsView), + reinterpret_cast(ArrayBuffer::Species), + reinterpret_cast(ArrayBuffer::GetByteLength), + reinterpret_cast(DataView::DataViewConstructor), + reinterpret_cast(DataView::GetFloat32), + reinterpret_cast(DataView::GetFloat64), + reinterpret_cast(DataView::GetInt8), + reinterpret_cast(DataView::GetInt16), + reinterpret_cast(DataView::GetInt32), + reinterpret_cast(DataView::GetUint8), + reinterpret_cast(DataView::GetUint16), + reinterpret_cast(DataView::GetUint32), + reinterpret_cast(DataView::SetFloat32), + reinterpret_cast(DataView::SetFloat64), + reinterpret_cast(DataView::SetInt8), + reinterpret_cast(DataView::SetInt16), + reinterpret_cast(DataView::SetInt32), + reinterpret_cast(DataView::SetUint8), + reinterpret_cast(DataView::SetUint16), + reinterpret_cast(DataView::SetUint32), + reinterpret_cast(DataView::GetBuffer), + reinterpret_cast(DataView::GetByteLength), + reinterpret_cast(DataView::GetOffset), + reinterpret_cast(Global::PrintEntrypoint), + reinterpret_cast(Global::NotSupportEval), + reinterpret_cast(Global::IsFinite), + reinterpret_cast(Global::IsNaN), + reinterpret_cast(Global::DecodeURI), + reinterpret_cast(Global::DecodeURIComponent), + reinterpret_cast(Global::EncodeURI), + reinterpret_cast(Global::EncodeURIComponent), + reinterpret_cast(Math::Abs), + reinterpret_cast(Math::Acos), + reinterpret_cast(Math::Acosh), + reinterpret_cast(Math::Asin), + reinterpret_cast(Math::Asinh), + reinterpret_cast(Math::Atan), + reinterpret_cast(Math::Atanh), + reinterpret_cast(Math::Atan2), + reinterpret_cast(Math::Cbrt), + reinterpret_cast(Math::Ceil), + reinterpret_cast(Math::Clz32), + reinterpret_cast(Math::Cos), + reinterpret_cast(Math::Cosh), + reinterpret_cast(Math::Exp), + reinterpret_cast(Math::Expm1), + reinterpret_cast(Math::Floor), + reinterpret_cast(Math::Fround), + reinterpret_cast(Math::Hypot), + reinterpret_cast(Math::Imul), + reinterpret_cast(Math::Log), + reinterpret_cast(Math::Log1p), + reinterpret_cast(Math::Log10), + reinterpret_cast(Math::Log2), + reinterpret_cast(Math::Max), + reinterpret_cast(Math::Min), + reinterpret_cast(Math::Pow), + reinterpret_cast(Math::Random), + reinterpret_cast(Math::Round), + reinterpret_cast(Math::Sign), + reinterpret_cast(Math::Sin), + reinterpret_cast(Math::Sinh), + reinterpret_cast(Math::Sqrt), + reinterpret_cast(Math::Tan), + reinterpret_cast(Math::Tanh), + reinterpret_cast(Math::Trunc), + reinterpret_cast(Json::Parse), + reinterpret_cast(Json::Stringify), + reinterpret_cast(BuiltinsIterator::Next), + reinterpret_cast(BuiltinsIterator::Return), + reinterpret_cast(BuiltinsIterator::Throw), + reinterpret_cast(BuiltinsIterator::GetIteratorObj), + reinterpret_cast(JSForInIterator::Next), + reinterpret_cast(JSSetIterator::Next), + reinterpret_cast(JSMapIterator::Next), + reinterpret_cast(JSArrayIterator::Next), + reinterpret_cast(Proxy::ProxyConstructor), + reinterpret_cast(Proxy::Revocable), + reinterpret_cast(Reflect::ReflectApply), + reinterpret_cast(Reflect::ReflectConstruct), + reinterpret_cast(Reflect::ReflectDefineProperty), + reinterpret_cast(Reflect::ReflectDeleteProperty), + reinterpret_cast(Reflect::ReflectGet), + reinterpret_cast(Reflect::ReflectGetOwnPropertyDescriptor), + reinterpret_cast(Reflect::ReflectGetPrototypeOf), + reinterpret_cast(Reflect::ReflectHas), + reinterpret_cast(Reflect::ReflectIsExtensible), + reinterpret_cast(Reflect::ReflectOwnKeys), + reinterpret_cast(Reflect::ReflectPreventExtensions), + reinterpret_cast(Reflect::ReflectSet), + reinterpret_cast(Reflect::ReflectSetPrototypeOf), + reinterpret_cast(AsyncFunction::AsyncFunctionConstructor), + reinterpret_cast(GeneratorObject::GeneratorPrototypeNext), + reinterpret_cast(GeneratorObject::GeneratorPrototypeReturn), + reinterpret_cast(GeneratorObject::GeneratorPrototypeThrow), + reinterpret_cast(GeneratorObject::GeneratorFunctionConstructor), + reinterpret_cast(Promise::PromiseConstructor), + reinterpret_cast(Promise::All), + reinterpret_cast(Promise::Race), + reinterpret_cast(Promise::Resolve), + reinterpret_cast(Promise::Reject), + reinterpret_cast(Promise::Catch), + reinterpret_cast(Promise::Then), + reinterpret_cast(Promise::GetSpecies), + reinterpret_cast(BuiltinsPromiseJob::PromiseReactionJob), + reinterpret_cast(BuiltinsPromiseJob::PromiseResolveThenableJob), + + // not builtins method + reinterpret_cast(JSFunction::PrototypeSetter), + reinterpret_cast(JSFunction::PrototypeGetter), + reinterpret_cast(JSFunction::NameGetter), + reinterpret_cast(JSFunction::LengthGetter), + reinterpret_cast(JSArray::LengthSetter), + reinterpret_cast(JSArray::LengthGetter), +}; + +SnapShotSerialize::SnapShotSerialize(EcmaVM *vm, bool serialize) : vm_(vm), serialize_(serialize) +{ + objectArraySize_ = OBJECT_SIZE_EXTEND_PAGE; + if (serialize_) { + addressSlot_ = ToUintPtr(vm_->GetRegionFactory()->Allocate(sizeof(uintptr_t) * OBJECT_SIZE_EXTEND_PAGE)); + } else { + addressSlot_ = ToUintPtr(vm_->GetRegionFactory()->Allocate(sizeof(uintptr_t) * objectArraySize_)); + } +} + +SnapShotSerialize::~SnapShotSerialize() +{ + if (serialize_) { + vm_->GetRegionFactory()->Free(ToVoidPtr(addressSlot_), sizeof(uintptr_t) * OBJECT_SIZE_EXTEND_PAGE); + } else { + vm_->GetRegionFactory()->Free(ToVoidPtr(addressSlot_), sizeof(uintptr_t) * objectArraySize_); + } +} + +void SnapShotSerialize::SetObjectSlotField(uintptr_t obj, size_t offset, uint64_t value) +{ + *reinterpret_cast(obj + offset) = value; +} + +void SnapShotSerialize::SetObjectSlotFieldUint32(uintptr_t obj, size_t offset, uint32_t value) +{ + *reinterpret_cast(obj + offset) = value; +} + +void SnapShotSerialize::Serialize(TaggedObject *objectHeader, CQueue *queue, + std::unordered_map *data) +{ + uint8_t objectType = SerializeHelper::GetObjectType(objectHeader); + size_t objectSize = objectHeader->GetObjectSize(); + if (objectSize > MAX_REGULAR_HEAP_OBJECT_SIZE) { + LOG_ECMA_MEM(FATAL) << "It is a large object. Not Support."; + } + + if (objectSize == 0) { + LOG_ECMA_MEM(FATAL) << "It is a zero object. Not Support."; + } + + uintptr_t snapshotObj = vm_->GetFactory()->NewSpaceBySnapShotAllocator(objectSize); + if (snapshotObj == 0) { + LOG_ECMA(DEBUG) << "SnapShotAllocator OOM"; + return; + } + if (memcpy_s(ToVoidPtr(snapshotObj), objectSize, objectHeader, objectSize) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + + // header + SlotBit headSlot = HandleObjectHeader(objectHeader, objectType, objectSize, queue, data); + SetObjectSlotField(snapshotObj, 0, headSlot.GetValue()); + + count_++; + LOG_IF(count_ > MAX_OBJECT_INDEX, FATAL, RUNTIME) << "objectCount: " + ToCString(count_); + LOG_IF(objectSize > MAX_OBJECT_SIZE_INDEX, FATAL, RUNTIME) << "objectSize: " + ToCString(objectSize); + switch (JSType(objectType)) { + case JSType::HCLASS: + DynClassSerialize(objectHeader, snapshotObj, objectSize, queue, data); + break; + case JSType::STRING: + DynStringSerialize(objectHeader, snapshotObj); + break; + case JSType::TAGGED_ARRAY: + case JSType::TAGGED_DICTIONARY: + DynArraySerialize(objectHeader, snapshotObj, queue, data); + break; + case JSType::JS_NATIVE_POINTER: + NativePointerSerialize(objectHeader, snapshotObj); + break; + case JSType::PROGRAM: + DynProgramSerialize(objectHeader, snapshotObj, queue, data); + break; + case JSType::JS_FUNCTION_BASE: + case JSType::JS_FUNCTION: + case JSType::JS_PROXY_REVOC_FUNCTION: + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + case JSType::JS_GENERATOR_FUNCTION: + case JSType::JS_ASYNC_FUNCTION: + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + case JSType::JS_BOUND_FUNCTION: + JSFunctionBaseSerialize(objectHeader, snapshotObj, objectSize, queue, data); + break; + case JSType::JS_PROXY: + JSProxySerialize(objectHeader, snapshotObj, objectSize, queue, data); + break; + default: + JSObjectSerialize(objectHeader, snapshotObj, objectSize, queue, data); + break; + } +} + +void SnapShotSerialize::ExtendObjectArray() +{ + int countNow = objectArraySize_; + objectArraySize_ = objectArraySize_ + OBJECT_SIZE_EXTEND_PAGE; + + auto addr = vm_->GetRegionFactory()->Allocate(sizeof(uintptr_t) * objectArraySize_); + int size = countNow * ADDRESS_SIZE; + if (memcpy_s(addr, size, ToVoidPtr(addressSlot_), size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + + vm_->GetRegionFactory()->Free(ToVoidPtr(addressSlot_), sizeof(uintptr_t) * objectArraySize_); + addressSlot_ = ToUintPtr(addr); +} + +void SnapShotSerialize::RedirectSlot(const panda_file::File *pf) +{ + SnapShotSpace *space = vm_->GetHeap()->GetSnapShotSpace(); + EcmaStringTable *stringTable = vm_->GetEcmaStringTable(); + + size_t others = 0; + space->EnumerateRegions([stringTable, &others, this, pf](Region *current) { + size_t allocated = current->GetAllocatedBytes(); + uintptr_t begin = current->GetBegin(); + uintptr_t end = begin + allocated; + while (begin < end) { + if (others != 0) { + for (size_t i = 0; i < others; i++) { + pandaMethod_.emplace_back(begin); + auto method = reinterpret_cast(begin); + method->SetPandaFile(pf); + method->SetBytecodeArray(method->GetInstructions()); + vm_->frameworkProgramMethods_.emplace_back(method); + begin += METHOD_SIZE; + if (begin >= end) { + others = others - i - 1; + } + } + break; + } + + if (count_ == objectArraySize_) { + ExtendObjectArray(); + } + SlotBit slot(*reinterpret_cast(begin)); + if (slot.GetObjectType() == MASK_METHOD_SPACE_BEGIN) { + begin += sizeof(uint64_t); + for (size_t i = 0; i < slot.GetObjectSize(); i++) { + pandaMethod_.emplace_back(begin); + auto method = reinterpret_cast(begin); + method->SetPandaFile(pf); + method->SetBytecodeArray(method->GetInstructions()); + vm_->frameworkProgramMethods_.emplace_back(method); + begin += METHOD_SIZE; + if (begin >= end) { + others = slot.GetObjectSize() - i - 1; + break; + } + } + break; + } + + if (JSType(slot.GetObjectType()) == JSType::STRING) { + stringTable->InsertStringIfNotExist(reinterpret_cast(begin)); + } + + SetAddressToSlot(count_, begin); + begin = begin + AlignUp(slot.GetObjectSize(), static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + count_++; + } + }); + + auto constants = const_cast(vm_->GetJSThread()->GlobalConstants()); + for (int i = 0; i < count_; i++) { + SlotBit slot(*GetAddress(i)); + size_t objectSize = slot.GetObjectSize(); + uint8_t objectType = slot.GetObjectType(); + size_t index = slot.GetObjectInConstantsIndex(); + + switch (JSType(objectType)) { + case JSType::HCLASS: + DynClassDeserialize(GetAddress(i)); + break; + case JSType::STRING: + DynStringDeserialize(GetAddress(i)); + break; + case JSType::TAGGED_ARRAY: + case JSType::TAGGED_DICTIONARY: + DynArrayDeserialize(GetAddress(i)); + break; + case JSType::JS_NATIVE_POINTER: + NativePointerDeserialize(GetAddress(i)); + break; + case JSType::GLOBAL_ENV: + JSObjectDeserialize(GetAddress(i), objectSize); + vm_->SetGlobalEnv(GetAddress(i)); + break; + case JSType::MICRO_JOB_QUEUE: + JSObjectDeserialize(GetAddress(i), objectSize); + vm_->SetMicroJobQueue(GetAddress(i)); + break; + case JSType::PROGRAM: + DynProgramDeserialize(GetAddress(i), objectSize); + vm_->frameworkProgram_ = JSTaggedValue(GetAddress(i)); + break; + case JSType::JS_FUNCTION_BASE: + case JSType::JS_FUNCTION: + case JSType::JS_PROXY_REVOC_FUNCTION: + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + case JSType::JS_GENERATOR_FUNCTION: + case JSType::JS_ASYNC_FUNCTION: + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + case JSType::JS_BOUND_FUNCTION: + JSFunctionBaseDeserialize(GetAddress(i), objectSize); + break; + case JSType::JS_PROXY: + JSProxyDeserialize(GetAddress(i), objectSize); + break; + default: + JSObjectDeserialize(GetAddress(i), objectSize); + break; + } + if (index != 0) { + JSTaggedValue result(GetAddress(i)); + constants->SetConstant(ConstantIndex(index - 1), result); + } + } +} + +SlotBit SnapShotSerialize::HandleObjectHeader(TaggedObject *objectHeader, uint8_t objectType, size_t objectSize, + CQueue *queue, + std::unordered_map *data) +{ + auto *hclassClass = objectHeader->GetClass(); + SlotBit slot(0); + + ASSERT(hclassClass != nullptr); + if (data->find(ToUintPtr(hclassClass)) == data->end()) { + slot = SerializeHelper::AddObjectHeaderToData(hclassClass, queue, data); + } else { + slot = data->find(ToUintPtr(hclassClass))->second; + } + + SlotBit objectSlotBit = data->find(ToUintPtr(objectHeader))->second; + slot.SetObjectInConstantsIndex(objectSlotBit.GetObjectInConstantsIndex()); + slot.SetObjectSize(objectSize); + slot.SetObjectType(objectType); + return slot; +} + +uint64_t SnapShotSerialize::HandleTaggedField(JSTaggedType *tagged, CQueue *queue, + std::unordered_map *data) +{ + JSTaggedValue taggedValue(*tagged); + if (taggedValue.IsWeak()) { + return JSTaggedValue::Undefined().GetRawData(); // Undefind + } + + if (taggedValue.IsSpecial()) { + SlotBit special(taggedValue.GetRawData()); + special.SetObjectSpecial(); + return special.GetValue(); // special slot + } + + if (!taggedValue.IsHeapObject()) { + return taggedValue.GetRawData(); // not object + } + + SlotBit slotBit(0); + if (data->find(*tagged) == data->end()) { + slotBit = SerializeHelper::AddObjectHeaderToData(taggedValue.GetTaggedObject(), queue, data); + } else { + slotBit = data->find(taggedValue.GetRawData())->second; + } + + if (taggedValue.IsString()) { + slotBit.SetReferenceToString(true); + } + return slotBit.GetValue(); // object +} + +void SnapShotSerialize::DeserializeHandleTaggedField(uint64_t *value) +{ + SlotBit slot(*value); + if (slot.IsReferenceSlot() && !slot.IsSpecial()) { + uint32_t index = slot.GetObjectIndex(); + + if (slot.IsReferenceToString()) { + auto str = vm_->GetEcmaStringTable()->GetString(GetAddress(index)); + ASSERT(str != nullptr); + *value = ToUintPtr(str); + } else { + *value = GetAddress(index); + } + return; + } + + if (slot.IsSpecial()) { + slot.ClearObjectSpecialFlag(); + *value = slot.GetValue(); + } +} + +void SnapShotSerialize::DeserializeHandleClassWord(TaggedObject *object) +{ + SlotBit slot(*reinterpret_cast(object)); + ASSERT(slot.IsReferenceSlot()); + uint32_t index = slot.GetObjectIndex(); + *reinterpret_cast(object) = 0; + + object->SetClass(GetAddress(index)); +} + +void SnapShotSerialize::DynClassSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, std::unordered_map *data) +{ + size_t beginOffset = JSHClass::PROTOTYPE_OFFSET; + int numOfFields = static_cast((objectSize - beginOffset) / TAGGED_SIZE); + uintptr_t startAddr = ToUintPtr(objectHeader) + beginOffset; + for (int i = 0; i < numOfFields; i++) { + auto fieldAddr = reinterpret_cast(startAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, beginOffset + i * TAGGED_SIZE, HandleTaggedField(fieldAddr, queue, data)); + } +} + +void SnapShotSerialize::DynClassDeserialize(uint64_t *objectHeader) +{ + auto dynClass = reinterpret_cast(objectHeader); + // handle object_header + DeserializeHandleClassWord(dynClass); + + uintptr_t startAddr = ToUintPtr(dynClass) + JSHClass::PROTOTYPE_OFFSET; + int numOfFields = static_cast((JSHClass::SIZE - JSHClass::PROTOTYPE_OFFSET) / TAGGED_SIZE); + for (int i = 0; i < numOfFields; i++) { + auto fieldAddr = reinterpret_cast(startAddr + i * TAGGED_SIZE); + DeserializeHandleTaggedField(fieldAddr); + } +} + +void SnapShotSerialize::DynStringSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj) +{ + auto *str = EcmaString::Cast(objectHeader); + SetObjectSlotFieldUint32(snapshotObj, OBJECT_HEADER_SIZE, str->GetLength() << 2U); +} + +void SnapShotSerialize::DynStringDeserialize(uint64_t *objectHeader) +{ + auto object = reinterpret_cast(objectHeader); + // handle object_header + DeserializeHandleClassWord(object); +} + +void SnapShotSerialize::DynArraySerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, + CQueue *queue, std::unordered_map *data) +{ + auto arrayObject = reinterpret_cast(objectHeader); + size_t beginOffset = TaggedArray::GetDataOffset(); + auto arrayLength = arrayObject->GetLength(); + uintptr_t startAddr = ToUintPtr(objectHeader) + beginOffset; + for (array_size_t i = 0; i < arrayLength; i++) { + auto fieldAddr = reinterpret_cast(startAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, beginOffset + i * TAGGED_SIZE, HandleTaggedField(fieldAddr, queue, data)); + } +} + +void SnapShotSerialize::DynArrayDeserialize(uint64_t *objectHeader) +{ + auto object = reinterpret_cast(objectHeader); + // handle object_header + DeserializeHandleClassWord(object); + + auto arrayLength = object->GetLength(); + size_t dataOffset = TaggedArray::GetDataOffset(); + uintptr_t startAddr = ToUintPtr(objectHeader) + dataOffset; + for (array_size_t i = 0; i < arrayLength; i++) { + auto fieldAddr = reinterpret_cast(startAddr + i * TAGGED_SIZE); + DeserializeHandleTaggedField(fieldAddr); + } +} + +SlotBit SnapShotSerialize::NativePointerToSlotBit(void *nativePointer) +{ + SlotBit native(0); + if (nativePointer != nullptr) { // nativePointer + uint32_t index = MAX_UINT_32; + + if (programSerialize_) { + pandaMethod_.emplace_back(ToUintPtr(nativePointer)); + // NOLINTNEXTLINE(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions) + index = pandaMethod_.size() + PROGRAM_NATIVE_METHOD_BEGIN + NATIVE_METHOD_SIZE - 1; + } else { + for (size_t i = 0; i < PROGRAM_NATIVE_METHOD_BEGIN; i++) { + if (nativePointer == reinterpret_cast(g_nativeTable[i + NATIVE_METHOD_SIZE])) { + index = i + NATIVE_METHOD_SIZE; + break; + } + } + + // not found + if (index == MAX_UINT_32) { + auto nativeMethod = reinterpret_cast(nativePointer)->GetNativePointer(); + for (size_t i = 0; i < NATIVE_METHOD_SIZE; i++) { + if (nativeMethod == GetAddress(i)) { + index = i; + break; + } + } + } + } + + ASSERT(index != MAX_UINT_32); + LOG_IF(index > MAX_C_POINTER_INDEX, FATAL, RUNTIME) << "MAX_C_POINTER_INDEX: " + ToCString(index); + native.SetObjectIndex(index); + } + return native; +} + +void *SnapShotSerialize::NativePointerSlotBitToAddr(SlotBit native) +{ + uint32_t index = native.GetObjectIndex(); + void *addr = nullptr; + + if (index < NATIVE_METHOD_SIZE) { + addr = reinterpret_cast(vm_->nativeMethods_.at(index)); + } else if (index < NATIVE_METHOD_SIZE + PROGRAM_NATIVE_METHOD_BEGIN) { + addr = reinterpret_cast(g_nativeTable[index]); + } else { + addr = ToVoidPtr(pandaMethod_.at(index - PROGRAM_NATIVE_METHOD_BEGIN - NATIVE_METHOD_SIZE)); + } + return addr; +} + +void SnapShotSerialize::NativePointerSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj) +{ + void *nativePointer = JSNativePointer::Cast(objectHeader)->GetExternalPointer(); + SetObjectSlotField(snapshotObj, OBJECT_HEADER_SIZE, NativePointerToSlotBit(nativePointer).GetValue()); +} + +void SnapShotSerialize::NativePointerDeserialize(uint64_t *objectHeader) +{ + auto object = reinterpret_cast(objectHeader); + // handle object_header + DeserializeHandleClassWord(object); + + size_t nativeAddr = ToUintPtr(object) + OBJECT_HEADER_SIZE; + SlotBit native(*reinterpret_cast(nativeAddr)); + if (native.GetObjectIndex() == MAX_OBJECT_INDEX) { + return; + } + JSNativePointer::Cast(object)->SetExternalPointer(NativePointerSlotBitToAddr(native)); +} + +void SnapShotSerialize::JSObjectSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, std::unordered_map *data) +{ + int numOfFields = static_cast((objectSize - OBJECT_HEADER_SIZE) / TAGGED_SIZE); + uintptr_t startAddr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + for (int i = 0; i < numOfFields; i++) { + auto fieldAddr = reinterpret_cast(startAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, OBJECT_HEADER_SIZE + i * TAGGED_SIZE, + HandleTaggedField(fieldAddr, queue, data)); + } +} + +void SnapShotSerialize::JSFunctionBaseSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, + std::unordered_map *data) +{ + // befour + int befourFields = static_cast((JSFunctionBase::METHOD_OFFSET - OBJECT_HEADER_SIZE) / TAGGED_SIZE); + uintptr_t befourStartAddr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + for (int i = 0; i < befourFields; i++) { + auto fieldAddr = reinterpret_cast(befourStartAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, OBJECT_HEADER_SIZE + i * TAGGED_SIZE, + HandleTaggedField(fieldAddr, queue, data)); + } + + // method + auto functionBase = static_cast(objectHeader); + size_t methodOffset = functionBase->OffsetMethod(); + auto nativePointer = reinterpret_cast(functionBase->GetMethod()); + SetObjectSlotField(snapshotObj, methodOffset, NativePointerToSlotBit(nativePointer).GetValue()); + + // after + size_t afterOffset = JSFunctionBase::METHOD_OFFSET + TAGGED_SIZE; + int afterFields = static_cast((objectSize - afterOffset) / TAGGED_SIZE); + uintptr_t afterStartAddr = ToUintPtr(objectHeader) + JSFunctionBase::METHOD_OFFSET + TAGGED_SIZE; + for (int i = 0; i < afterFields; i++) { + auto fieldAddr = reinterpret_cast(afterStartAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, afterOffset + i * TAGGED_SIZE, HandleTaggedField(fieldAddr, queue, data)); + } +} + +void SnapShotSerialize::JSProxySerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, std::unordered_map *data) +{ + // befour + int befourFields = static_cast((JSProxy::METHOD_OFFSET - OBJECT_HEADER_SIZE) / TAGGED_SIZE); + uintptr_t befourStartAddr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + for (int i = 0; i < befourFields; i++) { + auto fieldAddr = reinterpret_cast(befourStartAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, OBJECT_HEADER_SIZE + i * TAGGED_SIZE, + HandleTaggedField(fieldAddr, queue, data)); + } + + // method + auto jsproxy = static_cast(objectHeader); + size_t methodOffset = jsproxy->OffsetMethod(); + auto nativePointer = reinterpret_cast(jsproxy->GetMethod()); + SetObjectSlotField(snapshotObj, methodOffset, NativePointerToSlotBit(nativePointer).GetValue()); + + // after + size_t afterOffset = JSProxy::METHOD_OFFSET + TAGGED_SIZE; + int afterFields = static_cast((objectSize - afterOffset) / TAGGED_SIZE); + uintptr_t afterStartAddr = ToUintPtr(objectHeader) + JSProxy::METHOD_OFFSET + TAGGED_SIZE; + for (int i = 0; i < afterFields; i++) { + auto fieldAddr = reinterpret_cast(afterStartAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, afterOffset + i * TAGGED_SIZE, HandleTaggedField(fieldAddr, queue, data)); + } +} + +void SnapShotSerialize::DeserializeRangeTaggedField(size_t beginAddr, int numOfFields) +{ + for (int i = 0; i < numOfFields; i++) { + auto fieldAddr = reinterpret_cast(beginAddr + i * TAGGED_SIZE); + DeserializeHandleTaggedField(fieldAddr); + } +} + +void SnapShotSerialize::JSObjectDeserialize(uint64_t *objectHeader, size_t objectSize) +{ + auto object = reinterpret_cast(objectHeader); + // handle object_header + DeserializeHandleClassWord(object); + + auto objBodySize = objectSize - OBJECT_HEADER_SIZE; + ASSERT(objBodySize % TAGGED_SIZE == 0); + int numOfFields = static_cast(objBodySize / TAGGED_SIZE); + size_t addr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + DeserializeRangeTaggedField(addr, numOfFields); +} + +void SnapShotSerialize::JSFunctionBaseDeserialize(uint64_t *objectHeader, size_t objectSize) +{ + auto object = reinterpret_cast(objectHeader); + DeserializeHandleClassWord(object); + + // befour + auto befourMethod = JSFunctionBase::METHOD_OFFSET - OBJECT_HEADER_SIZE; + ASSERT(befourMethod % TAGGED_SIZE == 0); + int befourMethodFields = static_cast(befourMethod / TAGGED_SIZE); + size_t befourAddr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + DeserializeRangeTaggedField(befourAddr, befourMethodFields); + + // method + size_t nativeAddr = ToUintPtr(object) + JSFunctionBase::METHOD_OFFSET; + SlotBit native(*reinterpret_cast(nativeAddr)); + if (native.GetObjectIndex() != MAX_OBJECT_INDEX) { + object->SetMethod(reinterpret_cast(NativePointerSlotBitToAddr(native))); + } + + // after + auto afterMethod = objectSize - JSFunctionBase::METHOD_OFFSET - TAGGED_SIZE; + ASSERT(afterMethod % TAGGED_SIZE == 0); + int afterMethodFields = static_cast(afterMethod / TAGGED_SIZE); + size_t afterAddr = ToUintPtr(objectHeader) + JSFunctionBase::METHOD_OFFSET + TAGGED_SIZE; + DeserializeRangeTaggedField(afterAddr, afterMethodFields); +} + +void SnapShotSerialize::JSProxyDeserialize(uint64_t *objectHeader, size_t objectSize) +{ + auto object = reinterpret_cast(objectHeader); + DeserializeHandleClassWord(object); + + // befour + auto befourMethod = JSProxy::METHOD_OFFSET - OBJECT_HEADER_SIZE; + ASSERT(befourMethod % TAGGED_SIZE == 0); + int befourMethodFields = static_cast(befourMethod / TAGGED_SIZE); + size_t befourAddr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + DeserializeRangeTaggedField(befourAddr, befourMethodFields); + + // method + size_t nativeAddr = ToUintPtr(object) + JSProxy::METHOD_OFFSET; + SlotBit native(*reinterpret_cast(nativeAddr)); + if (native.GetObjectIndex() != MAX_OBJECT_INDEX) { + object->SetMethod(reinterpret_cast(NativePointerSlotBitToAddr(native))); + } + + // after + auto afterMethod = objectSize - JSProxy::METHOD_OFFSET - TAGGED_SIZE; + ASSERT(afterMethod % TAGGED_SIZE == 0); + int afterMethodFields = static_cast(afterMethod / TAGGED_SIZE); + size_t afterAddr = ToUintPtr(objectHeader) + JSProxy::METHOD_OFFSET + TAGGED_SIZE; + DeserializeRangeTaggedField(afterAddr, afterMethodFields); +} + +void SnapShotSerialize::DynProgramSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, + CQueue *queue, + std::unordered_map *data) +{ + size_t beginOffset = OBJECT_HEADER_SIZE; + auto objBodySize = Program::METHODS_DATA_OFFSET - OBJECT_HEADER_SIZE; + int numOfFields = static_cast((objBodySize) / TAGGED_SIZE); + uintptr_t startAddr = ToUintPtr(objectHeader) + beginOffset; + for (int i = 0; i < numOfFields; i++) { + auto fieldAddr = reinterpret_cast(startAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, beginOffset + i * TAGGED_SIZE, HandleTaggedField(fieldAddr, queue, data)); + } + SetObjectSlotField(snapshotObj, Program::METHODS_DATA_OFFSET, 0); // methods + SetObjectSlotField(snapshotObj, Program::NUMBER_METHODS_OFFSET, 0); // method_number +} + +void SnapShotSerialize::DynProgramDeserialize(uint64_t *objectHeader, [[maybe_unused]] size_t objectSize) +{ + auto object = reinterpret_cast(objectHeader); + // handle object_header + DeserializeHandleClassWord(object); + + auto objBodySize = Program::METHODS_DATA_OFFSET - OBJECT_HEADER_SIZE; + ASSERT(objBodySize % TAGGED_SIZE == 0); + int numOfFields = static_cast(objBodySize / TAGGED_SIZE); + size_t addr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + for (int i = 0; i < numOfFields; i++) { + auto fieldAddr = reinterpret_cast(addr + i * TAGGED_SIZE); + DeserializeHandleTaggedField(fieldAddr); + } + + auto program = reinterpret_cast(objectHeader); + program->SetMethodsData(nullptr); + program->SetNumberMethods(0); +} + +void SnapShotSerialize::SerializePandaFileMethod() +{ + SlotBit slot(0); + slot.SetObjectType(MASK_METHOD_SPACE_BEGIN); + slot.SetObjectSize(pandaMethod_.size()); + + ObjectFactory *factory = vm_->GetFactory(); + // panda method space begin + uintptr_t snapshotObj = factory->NewSpaceBySnapShotAllocator(sizeof(uint64_t)); + if (snapshotObj == 0) { + LOG(ERROR, RUNTIME) << "SnapShotAllocator OOM"; + return; + } + SetObjectSlotField(snapshotObj, 0, slot.GetValue()); // methods + + // panda methods + for (auto &it : pandaMethod_) { + // write method + size_t methodObjSize = METHOD_SIZE; + uintptr_t methodObj = factory->NewSpaceBySnapShotAllocator(methodObjSize); + if (methodObj == 0) { + LOG(ERROR, RUNTIME) << "SnapShotAllocator OOM"; + return; + } + if (memcpy_s(ToVoidPtr(methodObj), methodObjSize, ToVoidPtr(it), METHOD_SIZE) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } +} + +void SnapShotSerialize::RegisterNativeMethod() // NOLINT(readability-function-size) +{ + constexpr int size = sizeof(g_nativeTable) / sizeof(uintptr_t); + ASSERT(size == NATIVE_METHOD_SIZE + PROGRAM_NATIVE_METHOD_BEGIN); + for (int i = 0; i < size; i++) { + SetAddressToSlot(i, g_nativeTable[i]); + } +} + +void SnapShotSerialize::GeneratedNativeMethod() // NOLINT(readability-function-size) +{ + for (int i = 0; i < NATIVE_METHOD_SIZE; i++) { + SetAddressToSlot(i, g_nativeTable[i]); + vm_->GetMethodForNativeFunction(reinterpret_cast(g_nativeTable[i])); + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/snapshot/mem/snapshot_serialize.h b/ecmascript/snapshot/mem/snapshot_serialize.h new file mode 100644 index 0000000000000000000000000000000000000000..0aadd70ea04a419f87d309584cff4ba46cd74e4d --- /dev/null +++ b/ecmascript/snapshot/mem/snapshot_serialize.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_ECMASCRIPT_MEM_SNAPSHOT_SERIALIZE_H +#define PANDA_ECMASCRIPT_MEM_SNAPSHOT_SERIALIZE_H + +#include +#include +#include + +#include "libpandabase/macros.h" +#include "ecmascript/snapshot/mem/slot_bit.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda::ecmascript { +class EcmaVM; + +class SnapShotSerialize final { +public: + explicit SnapShotSerialize(EcmaVM *vm, bool serialize); + ~SnapShotSerialize(); + + void Serialize(TaggedObject *objectHeader, CQueue *queue, + std::unordered_map *data); + void RedirectSlot(const panda_file::File *pf); + void SerializePandaFileMethod(); + + void SetProgramSerializeStart() + { + programSerialize_ = true; + } + + void RegisterNativeMethod(); + void GeneratedNativeMethod(); + +private: + void SetObjectSlotField(uintptr_t obj, size_t offset, uint64_t value); + void SetObjectSlotFieldUint32(uintptr_t obj, size_t offset, uint32_t value); + + void NativePointerSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj); + void JSObjectSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, + std::unordered_map *data); + void JSFunctionBaseSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, + std::unordered_map *data); + void JSProxySerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, + std::unordered_map *data); + void DynClassSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, + std::unordered_map *data); + void DynArraySerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, CQueue *queue, + std::unordered_map *data); + void DynStringSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj); + void DynProgramSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, CQueue *queue, + std::unordered_map *data); + + void NativePointerDeserialize(uint64_t *objectHeader); + void JSObjectDeserialize(uint64_t *objectHeader, size_t objectSize); + void JSFunctionBaseDeserialize(uint64_t *objectHeader, size_t objectSize); + void JSProxyDeserialize(uint64_t *objectHeader, size_t objectSize); + void DynClassDeserialize(uint64_t *objectHeader); + void DynStringDeserialize(uint64_t *objectHeader); + void DynArrayDeserialize(uint64_t *objectHeader); + void DynProgramDeserialize(uint64_t *objectHeader, size_t objectSize); + + SlotBit HandleObjectHeader(TaggedObject *objectHeader, uint8_t objectType, size_t objectSize, + CQueue *queue, + std::unordered_map *data); + uint64_t HandleTaggedField(JSTaggedType *tagged, CQueue *queue, + std::unordered_map *data); + void DeserializeHandleTaggedField(uint64_t *value); + void DeserializeHandleClassWord(TaggedObject *object); + void ExtendObjectArray(); + + void SetAddressToSlot(size_t index, uintptr_t value) + { + auto addr = reinterpret_cast(addressSlot_ + index * ADDRESS_SIZE); + *addr = value; + } + + template + T GetAddress(size_t index) + { + return *reinterpret_cast(addressSlot_ + index * ADDRESS_SIZE); + } + + SlotBit NativePointerToSlotBit(void *nativePointer); + void *NativePointerSlotBitToAddr(SlotBit native); + void DeserializeRangeTaggedField(size_t beginAddr, int numOfFields); + + EcmaVM *vm_{nullptr}; + bool serialize_{false}; + bool programSerialize_{false}; + int count_{0}; + int objectArraySize_{0}; + uintptr_t addressSlot_; + CVector pandaMethod_; + + NO_COPY_SEMANTIC(SnapShotSerialize); + NO_MOVE_SEMANTIC(SnapShotSerialize); +}; +} // namespace panda::ecmascript + +#endif // PANDA_ECMASCRIPT_MEM_SNAPSHOT_SERIALIZE_H diff --git a/ecmascript/snapshot/tests/BUILD.gn b/ecmascript/snapshot/tests/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..476ea6d4211991eb529f96b3b4776ec0e32d0911 --- /dev/null +++ b/ecmascript/snapshot/tests/BUILD.gn @@ -0,0 +1,50 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//ark/js_runtime/test/test_helper.gni") +import("//build/test.gni") + +module_output_path = "ark/js_runtime" + +host_unittest_action("SnapshotTest") { + module_out_path = module_output_path + + sources = [ + # test file + "snapshot_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +group("unittest") { + testonly = true + deps = [ ":SnapshotTest" ] +} + +group("host_unittest") { + testonly = true + deps = [ ":SnapshotTestAction(${host_toolchain})" ] +} diff --git a/ecmascript/snapshot/tests/snapshot_test.cpp b/ecmascript/snapshot/tests/snapshot_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3131e0baac3e18970a95c7d8d0cdf9754a90d3dc --- /dev/null +++ b/ecmascript/snapshot/tests/snapshot_test.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tests/test_helper.h" + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/snapshot/mem/snapshot.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class SnapShotTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + RuntimeOptions options; + options.SetShouldLoadBootPandaFiles(false); + options.SetShouldInitializeIntrinsics(false); + options.SetBootIntrinsicSpaces({"ecmascript"}); + options.SetBootClassSpaces({"ecmascript"}); + options.SetRuntimeType("ecmascript"); + options.SetRunGcInPlace(true); + ecmaVm = EcmaVM::Create(options); + ASSERT_TRUE(ecmaVm != nullptr) << "Cannot create Runtime"; + thread = ecmaVm->GetJSThread(); + scope = new EcmaHandleScope(thread); + thread->SetIsEcmaInterpreter(true); + ecmaVm->GetFactory()->SetTriggerGc(true); + } + + void TearDown() override + { + thread->ClearException(); + ecmaVm->GetFactory()->SetTriggerGc(false); + delete scope; + [[maybe_unused]] bool success = EcmaVM::Destroy(ecmaVm); + ASSERT_TRUE(success) << "Cannot destroy Runtime"; + } + + EcmaVM *ecmaVm {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; +} // namespace panda::test diff --git a/ecmascript/symbol_table-inl.h b/ecmascript/symbol_table-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..6ca03f5a28aa2649f3acc64b301a7c9e4a882994 --- /dev/null +++ b/ecmascript/symbol_table-inl.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_SYMBOL_TABLE_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_SYMBOL_TABLE_INL_H + +#include "symbol_table.h" +#include "js_symbol.h" +#include "tagged_hash_table-inl.h" +#include "libpandabase/utils/hash.h" + +namespace panda::ecmascript { +int SymbolTable::Hash(const JSTaggedValue &obj) +{ + if (obj.IsHeapObject()) { + if (obj.IsString()) { + auto *nameString = static_cast(obj.GetTaggedObject()); + return static_cast(nameString->GetHashcode()); + } + return static_cast(JSSymbol::ComputeHash()); + } + UNREACHABLE(); +} + +bool SymbolTable::IsMatch(const JSTaggedValue &name, const JSTaggedValue &other) +{ + if (name.IsHole() || name.IsUndefined()) { + return false; + } + + auto *nameString = static_cast(name.GetTaggedObject()); + auto *otherString = static_cast(other.GetTaggedObject()); + return EcmaString::StringsAreEqual(nameString, otherString); +} + +bool SymbolTable::ContainsKey(JSThread *thread, const JSTaggedValue &key) +{ + int entry = FindEntry(key); + return entry != -1; +} + +JSTaggedValue SymbolTable::GetSymbol(const JSTaggedValue &key) +{ + int entry = FindEntry(key); + ASSERT(entry != -1); + return GetValue(entry); +} + +JSTaggedValue SymbolTable::FindSymbol(JSThread *thread, const JSTaggedValue &value) +{ + JSSymbol *symbol = JSSymbol::Cast(value.GetTaggedObject()); + JSTaggedValue des = symbol->GetDescription(); + if (!des.IsUndefined()) { + if (ContainsKey(thread, des)) { + return des; + } + } + return JSTaggedValue::Undefined(); +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_SYMBOL_TABLE_INL_H \ No newline at end of file diff --git a/ecmascript/symbol_table.h b/ecmascript/symbol_table.h new file mode 100644 index 0000000000000000000000000000000000000000..cdafcf1e8c1c05bfd4325feefef1d745c3c087f3 --- /dev/null +++ b/ecmascript/symbol_table.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_SYMBOL_TABLE_H +#define PANDA_RUNTIME_ECMASCRIPT_SYMBOL_TABLE_H + +#include "ecmascript/tagged_hash_table.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/js_thread.h" + +namespace panda::ecmascript { +class SymbolTable : public TaggedHashTable { +public: + using HashTable = TaggedHashTable; + static SymbolTable *Cast(ObjectHeader *object) + { + return reinterpret_cast(object); + } + inline static int GetKeyIndex(int entry) + { + return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + static inline bool IsMatch(const JSTaggedValue &name, const JSTaggedValue &other); + static inline int Hash(const JSTaggedValue &obj); + + static const int DEFAULT_ELEMENTS_NUMBER = 64; + static SymbolTable *Create(JSThread *thread, int numberOfElements = DEFAULT_ELEMENTS_NUMBER) + { + return HashTable::Create(thread, numberOfElements); + } + + inline bool ContainsKey(JSThread *thread, const JSTaggedValue &key); + + inline JSTaggedValue GetSymbol(const JSTaggedValue &key); + + inline JSTaggedValue FindSymbol(JSThread *thread, const JSTaggedValue &value); + static constexpr int ENTRY_KEY_INDEX = 0; + static constexpr int ENTRY_VALUE_INDEX = 1; + static constexpr int ENTRY_SIZE = 2; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_SYMBOL_TABLE_H \ No newline at end of file diff --git a/ecmascript/tagged_array-inl.h b/ecmascript/tagged_array-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..d2ffd6f86ea401237fd35a719b842fc6cae0f2b8 --- /dev/null +++ b/ecmascript/tagged_array-inl.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TAGGED_ARRAY_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_TAGGED_ARRAY_INL_H + +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/tagged_array.h" + +namespace panda::ecmascript { +inline array_size_t TaggedArray::GetLength() const +{ + return Barriers::GetDynValue(this, TaggedArray::GetLengthOffset()); +} + +inline JSTaggedValue TaggedArray::Get(array_size_t idx) const +{ + ASSERT(idx < GetLength()); + // Note: Here we can't statically decide the element type is a primitive or heap object, especially for + // dynamically-typed languages like JavaScript. So we simply skip the read-barrier. + size_t offset = JSTaggedValue::TaggedTypeSize() * idx; + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + return JSTaggedValue(Barriers::GetDynValue(GetData(), offset)); +} + +inline JSTaggedValue TaggedArray::Get([[maybe_unused]] const JSThread *thread, array_size_t idx) const +{ + return Get(idx); +} + +inline array_size_t TaggedArray::GetIdx(const JSTaggedValue &value) const +{ + array_size_t length = GetLength(); + + for (array_size_t i = 0; i < length; i++) { + if (JSTaggedValue::SameValue(Get(i), value)) { + return i; + } + } + return TaggedArray::MAX_ARRAY_INDEX; +} + +template +inline void TaggedArray::Set(const JSThread *thread, array_size_t idx, const JSHandle &value) +{ + ASSERT(idx < GetLength()); + size_t offset = JSTaggedValue::TaggedTypeSize() * idx; + + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + if (value.GetTaggedValue().IsHeapObject()) { + Barriers::SetDynObject(thread, GetData(), offset, value.GetTaggedValue().GetRawData()); + } else { // NOLINTNEXTLINE(readability-misleading-indentation) + Barriers::SetDynPrimitive(GetData(), offset, value.GetTaggedValue().GetRawData()); + } +} + +inline void TaggedArray::Set(const JSThread *thread, array_size_t idx, const JSTaggedValue &value) +{ + ASSERT(idx < GetLength()); + size_t offset = JSTaggedValue::TaggedTypeSize() * idx; + + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + if (value.IsHeapObject()) { + Barriers::SetDynObject(thread, GetData(), offset, value.GetRawData()); + } else { // NOLINTNEXTLINE(readability-misleading-indentation) + Barriers::SetDynPrimitive(GetData(), offset, value.GetRawData()); + } +} + +JSHandle TaggedArray::Append(const JSThread *thread, const JSHandle &first, + const JSHandle &second) +{ + array_size_t firstLength = first->GetLength(); + array_size_t secondLength = second->GetLength(); + array_size_t length = firstLength + secondLength; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle argument = factory->NewTaggedArray(length); + array_size_t index = 0; + for (; index < firstLength; ++index) { + argument->Set(thread, index, first->Get(index)); + } + for (; index < length; ++index) { + argument->Set(thread, index, second->Get(index - firstLength)); + } + return argument; +} + +JSHandle TaggedArray::AppendSkipHole(const JSThread *thread, const JSHandle &first, + const JSHandle &second, array_size_t copyLength) +{ + array_size_t firstLength = first->GetLength(); + array_size_t secondLength = second->GetLength(); + ASSERT(firstLength + secondLength >= copyLength); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle argument = factory->NewTaggedArray(copyLength); + array_size_t index = 0; + for (; index < firstLength; ++index) { + JSTaggedValue val = first->Get(index); + if (val.IsHole()) { + break; + } + argument->Set(thread, index, val); + ASSERT(copyLength >= index); + } + for (array_size_t i = 0; i < secondLength; ++i) { + JSTaggedValue val = second->Get(i); + if (val.IsHole()) { + break; + } + argument->Set(thread, index++, val); + ASSERT(copyLength >= index); + } + return argument; +} + +inline bool TaggedArray::HasDuplicateEntry() const +{ + array_size_t length = GetLength(); + for (array_size_t i = 0; i < length; i++) { + for (array_size_t j = i + 1; j < length; j++) { + if (JSTaggedValue::SameValue(Get(i), Get(j))) { + return true; + } + } + } + return false; +} + +void TaggedArray::InitializeWithSpecialValue(JSTaggedValue initValue, array_size_t length) +{ + ASSERT(initValue.IsSpecial()); + SetLength(length); + for (array_size_t i = 0; i < length; i++) { + size_t offset = JSTaggedValue::TaggedTypeSize() * i; + Barriers::SetDynPrimitive(GetData(), offset, initValue.GetRawData()); + } +} + +inline JSHandle TaggedArray::SetCapacity(const JSThread *thread, const JSHandle &array, + array_size_t capa) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + array_size_t oldLength = array->GetLength(); + JSHandle newArray = factory->CopyArray(array, oldLength, capa); + return newArray; +} + +inline bool TaggedArray::IsDictionaryMode() const +{ + return GetClass()->IsDictionary(); +} + +void TaggedArray::Trim(JSThread *thread, array_size_t newLength) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + array_size_t oldLength = GetLength(); + ASSERT(oldLength > newLength); + size_t trimBytes = (oldLength - newLength) * JSTaggedValue::TaggedTypeSize(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); + factory->FillFreeObject(ToUintPtr(this) + size, trimBytes, RemoveSlots::YES); + SetLength(newLength); +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_TAGGED_ARRAY_INL_H diff --git a/ecmascript/tagged_array.h b/ecmascript/tagged_array.h new file mode 100644 index 0000000000000000000000000000000000000000..68827987b03ca0a83ed40967c8aefdd39c178214 --- /dev/null +++ b/ecmascript/tagged_array.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TAGGED_ARRAY_H +#define PANDA_RUNTIME_ECMASCRIPT_TAGGED_ARRAY_H + +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" + +namespace panda::ecmascript { +class ObjectFactory; + +class TaggedArray : public TaggedObject { +public: + static constexpr array_size_t MAX_ARRAY_INDEX = std::numeric_limits::max(); + + inline static TaggedArray *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsTaggedArray()); + return static_cast(obj); + } + + array_size_t GetLength() const; + + JSTaggedValue Get(array_size_t idx) const; + + array_size_t GetIdx(const JSTaggedValue &value) const; + + template + void Set(const JSThread *thread, array_size_t idx, const JSHandle &value); + + JSTaggedValue Get(const JSThread *thread, array_size_t idx) const; + + void Set(const JSThread *thread, array_size_t idx, const JSTaggedValue &value); + + static inline JSHandle Append(const JSThread *thread, const JSHandle &first, + const JSHandle &second); + static inline JSHandle AppendSkipHole(const JSThread *thread, const JSHandle &first, + const JSHandle &second, array_size_t copyLength); + + static inline size_t ComputeSize(size_t elemSize, array_size_t length) + { + ASSERT(elemSize != 0); + size_t size = sizeof(TaggedArray) + elemSize * length; +#ifdef PANDA_TARGET_32 + size_t sizeLimit = (std::numeric_limits::max() - sizeof(TaggedArray)) / elemSize; + if (UNLIKELY(sizeLimit < static_cast(length))) { + return 0; + } +#endif + return size; + } + + JSTaggedType *GetData() + { + return data_; + } + + const JSTaggedType *GetData() const + { + return data_; + } + + inline bool IsDictionaryMode() const; + + bool HasDuplicateEntry() const; + + static JSHandle SetCapacity(const JSThread *thread, const JSHandle &array, + array_size_t capa); + + static constexpr uint32_t GetLengthOffset() + { + return MEMBER_OFFSET(TaggedArray, length_); + } + + static constexpr uint32_t GetDataOffset() + { + return MEMBER_OFFSET(TaggedArray, data_); + } + + inline void InitializeWithSpecialValue(JSTaggedValue initValue, array_size_t length); + + inline void Trim(JSThread *thread, array_size_t newLength); + void Visitor(const EcmaObjectRangeVisitor &v) + { + uintptr_t dataAddr = ToUintPtr(this) + TaggedArray::GetDataOffset(); + v(this, ObjectSlot(dataAddr), ObjectSlot(dataAddr + GetLength() * JSTaggedValue::TaggedTypeSize())); + } + +private: + friend class ObjectFactory; + + void SetLength(array_size_t length) + { + length_ = length; + } + + array_size_t length_; + uint32_t placeholder_; // Add for 8bits align of data_ + // should align by 8, 32bit using TaggedType also + __extension__ alignas(sizeof(JSTaggedType)) JSTaggedType data_[0]; // NOLINT(modernize-avoid-c-arrays) +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_TAGGED_ARRAY_H diff --git a/ecmascript/tagged_dictionary.cpp b/ecmascript/tagged_dictionary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..86424872bf0c02723f61845c682643d38ec6101d --- /dev/null +++ b/ecmascript/tagged_dictionary.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "tagged_dictionary.h" +#include "tagged_hash_table-inl.h" + +namespace panda::ecmascript { +int NameDictionary::Hash(const JSTaggedValue &key) +{ + if (key.IsHeapObject()) { + JSTaggedValue jsKey(key); + if (jsKey.IsSymbol()) { + auto symbolString = JSSymbol::Cast(key.GetTaggedObject()); + return static_cast(symbolString->GetHashField()).GetInt(); + } + if (jsKey.IsString()) { + auto keyString = reinterpret_cast(key.GetTaggedObject()); + return keyString->GetHashcode(); + } + } + // key must be object + UNREACHABLE(); +} + +bool NameDictionary::IsMatch(const JSTaggedValue &key, const JSTaggedValue &other) +{ + return key == other; +} + +void NameDictionary::GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const +{ + int arrayIndex = 0; + int size = Size(); + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyAttributes attr = GetAttributes(hashIndex); + std::pair pair(key, attr); + sortArr.push_back(pair); + } + } + std::sort(sortArr.begin(), sortArr.end(), CompKey); + for (const auto &entry : sortArr) { + keyArray->Set(thread, arrayIndex + offset, entry.first); + arrayIndex++; + } +} + +void NameDictionary::GetAllEnumKeys(const JSThread *thread, int offset, TaggedArray *keyArray, array_size_t *keys) const +{ + array_size_t arrayIndex = 0; + int size = Size(); + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = GetKey(hashIndex); + if (key.IsString()) { + PropertyAttributes attr = GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + std::pair pair(key, attr); + sortArr.push_back(pair); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), CompKey); + for (auto entry : sortArr) { + keyArray->Set(thread, arrayIndex + offset, entry.first); + arrayIndex++; + } + *keys += arrayIndex; +} + +NameDictionary *NameDictionary::Create(const JSThread *thread, int numberOfElements) +{ + return OrderHashTableT::Create(thread, numberOfElements); +} + +PropertyAttributes NameDictionary::GetAttributes(int entry) const +{ + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + return PropertyAttributes(Get(index).GetInt()); +} + +void NameDictionary::SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData) +{ + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + Set(thread, index, metaData.GetTaggedValue()); +} + +void NameDictionary::SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, + const PropertyAttributes &metaData) +{ + SetKey(thread, entry, key); + SetValue(thread, entry, value); + SetAttributes(thread, entry, metaData); +} + +void NameDictionary::UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, + const PropertyAttributes &metaData) +{ + SetValue(thread, entry, value); + SetAttributes(thread, entry, metaData); +} + +void NameDictionary::UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value) +{ + SetValue(thread, entry, value); +} + +void NameDictionary::ClearEntry(const JSThread *thread, int entry) +{ + JSTaggedValue hole = JSTaggedValue::Hole(); + PropertyAttributes metaData; + SetEntry(thread, entry, hole, hole, metaData); +} + +int NumberDictionary::Hash(const JSTaggedValue &key) +{ + if (key.IsInt()) { + int keyValue = key.GetInt(); + return GetHash32(reinterpret_cast(&keyValue), sizeof(keyValue) / sizeof(uint8_t)); + } + // key must be object + UNREACHABLE(); +} + +bool NumberDictionary::IsMatch(const JSTaggedValue &key, const JSTaggedValue &other) +{ + if (key.IsHole() || key.IsUndefined()) { + return false; + } + + if (key.IsInt()) { + if (other.IsInt()) { + return key.GetInt() == other.GetInt(); + } + return false; + } + // key must be integer + UNREACHABLE(); +} + +void NumberDictionary::GetAllKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray) +{ + ASSERT_PRINT(offset + obj->EntriesCount() <= static_cast(keyArray->GetLength()), + "keyArray capacity is not enough for dictionary"); + int arrayIndex = 0; + int size = obj->Size(); + CVector sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = obj->GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + sortArr.push_back(JSTaggedValue(static_cast(key.GetInt()))); + } + } + std::sort(sortArr.begin(), sortArr.end(), CompKey); + for (auto entry : sortArr) { + JSHandle keyHandle(thread, entry); + JSHandle str = JSTaggedValue::ToString(const_cast(thread), keyHandle); + ASSERT_NO_ABRUPT_COMPLETION(thread); + keyArray->Set(thread, arrayIndex + offset, str.GetTaggedValue()); + arrayIndex++; + } +} + +void NumberDictionary::GetAllEnumKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray, array_size_t *keys) +{ + ASSERT_PRINT(offset + obj->EntriesCount() <= static_cast(keyArray->GetLength()), + "keyArray capacity is not enough for dictionary"); + array_size_t arrayIndex = 0; + int size = obj->Size(); + CVector sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = obj->GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyAttributes attr = obj->GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + sortArr.push_back(JSTaggedValue(static_cast(key.GetInt()))); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), CompKey); + for (auto entry : sortArr) { + JSHandle key_handle(thread, entry); + JSHandle str = JSTaggedValue::ToString(const_cast(thread), key_handle); + ASSERT_NO_ABRUPT_COMPLETION(thread); + keyArray->Set(thread, arrayIndex + offset, str.GetTaggedValue()); + arrayIndex++; + } + *keys += arrayIndex; +} + +NumberDictionary *NumberDictionary::Create(const JSThread *thread, int numberOfElements) +{ + return OrderHashTableT::Create(thread, numberOfElements); +} + +PropertyAttributes NumberDictionary::GetAttributes(int entry) const +{ + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + return PropertyAttributes(Get(index).GetInt()); +} + +void NumberDictionary::SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData) +{ + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + Set(thread, index, metaData.GetTaggedValue()); +} + +void NumberDictionary::SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, + const PropertyAttributes &metaData) +{ + SetKey(thread, entry, key); + SetValue(thread, entry, value); + SetAttributes(thread, entry, metaData); +} + +void NumberDictionary::UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, + const PropertyAttributes &metaData) +{ + SetValue(thread, entry, value); + SetAttributes(thread, entry, metaData); +} + +void NumberDictionary::UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value) +{ + SetValue(thread, entry, value); +} + +void NumberDictionary::ClearEntry(const JSThread *thread, int entry) +{ + JSTaggedValue hole = JSTaggedValue::Hole(); + PropertyAttributes metaData; + SetEntry(thread, entry, hole, hole, metaData); +} +} // namespace panda::ecmascript diff --git a/ecmascript/tagged_dictionary.h b/ecmascript/tagged_dictionary.h new file mode 100644 index 0000000000000000000000000000000000000000..d69022e9935b92e0c4219764f950bcda111ddde9 --- /dev/null +++ b/ecmascript/tagged_dictionary.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TAGGED_DICTIONARY_H +#define PANDA_RUNTIME_ECMASCRIPT_TAGGED_DICTIONARY_H + +#include "ecmascript/tagged_hash_table.h" + +namespace panda::ecmascript { +class NameDictionary : public OrderTaggedHashTable { +public: + using OrderHashTableT = OrderTaggedHashTable; + inline static int GetKeyIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + static int Hash(const JSTaggedValue &key); + static bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &other); + static NameDictionary *Create(const JSThread *thread, + int numberOfElements = OrderHashTableT::DEFAULT_ELEMENTS_NUMBER); + // Returns the property metaData for the property at entry. + PropertyAttributes GetAttributes(int entry) const; + void SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData); + void SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, + const PropertyAttributes &metaData); + void UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, + const PropertyAttributes &metaData); + void UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value); + void UpdateAttributes(int entry, const PropertyAttributes &metaData); + void ClearEntry(const JSThread *thread, int entry); + void GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const; + void GetAllEnumKeys(const JSThread *thread, int offset, TaggedArray *keyArray, array_size_t *keys) const; + static inline bool CompKey(const std::pair &a, + const std::pair &b) + { + return a.second.GetDictionaryOrder() < b.second.GetDictionaryOrder(); + } + DECL_DUMP() + + static constexpr int ENTRY_KEY_INDEX = 0; + static constexpr int ENTRY_VALUE_INDEX = 1; + static constexpr int ENTRY_DETAILS_INDEX = 2; + static constexpr int ENTRY_SIZE = 3; +}; + +class NumberDictionary : public OrderTaggedHashTable { +public: + using OrderHashTableT = OrderTaggedHashTable; + inline static int GetKeyIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + static int Hash(const JSTaggedValue &key); + static bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &other); + static NumberDictionary *Create(const JSThread *thread, + int numberOfElements = OrderHashTableT::DEFAULT_ELEMENTS_NUMBER); + // Returns the property metaData for the property at entry. + PropertyAttributes GetAttributes(int entry) const; + void SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData); + void SetEntry([[maybe_unused]] const JSThread *thread, int entry, const JSTaggedValue &key, + const JSTaggedValue &value, const PropertyAttributes &metaData); + void UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, + const PropertyAttributes &metaData); + void UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value); + void UpdateAttributes(int entry, const PropertyAttributes &metaData); + void ClearEntry(const JSThread *thread, int entry); + + static void GetAllKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray); + static void GetAllEnumKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray, array_size_t *keys); + static inline bool CompKey(const JSTaggedValue &a, const JSTaggedValue &b) + { + ASSERT(a.IsNumber() && b.IsNumber()); + return a.GetNumber() < b.GetNumber(); + } + DECL_DUMP() + + static constexpr int ENTRY_KEY_INDEX = 0; + static constexpr int ENTRY_VALUE_INDEX = 1; + static constexpr int ENTRY_DETAILS_INDEX = 2; + static constexpr int ENTRY_SIZE = 3; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_NEW_DICTIONARY_H diff --git a/ecmascript/tagged_hash_table-inl.h b/ecmascript/tagged_hash_table-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..0a3823ea087083000a2c3e362a6a9522558c733c --- /dev/null +++ b/ecmascript/tagged_hash_table-inl.h @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TAGGED_HASH_TABLE_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_TAGGED_HASH_TABLE_INL_H + +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tagged_hash_table.h" + +namespace panda::ecmascript { +template +int TaggedHashTable::EntriesCount() const +{ + return Get(NUMBER_OF_ENTRIES_INDEX).GetInt(); +} + +template +int TaggedHashTable::HoleEntriesCount() const +{ + return Get(NUMBER_OF_HOLE_ENTRIES_INDEX).GetInt(); +} + +template +int TaggedHashTable::Size() const +{ + return Get(SIZE_INDEX).GetInt(); +} + +template +void TaggedHashTable::IncreaseEntries(const JSThread *thread) +{ + SetEntriesCount(thread, EntriesCount() + 1); +} + +template +void TaggedHashTable::IncreaseHoleEntriesCount(const JSThread *thread, int number) +{ + SetEntriesCount(thread, EntriesCount() - number); + SetHoleEntriesCount(thread, HoleEntriesCount() + number); +} + +template +void TaggedHashTable::SetEntriesCount(const JSThread *thread, int nof) +{ + Set(thread, NUMBER_OF_ENTRIES_INDEX, JSTaggedValue(nof)); +} + +template +void TaggedHashTable::SetHoleEntriesCount(const JSThread *thread, int nod) +{ + Set(thread, NUMBER_OF_HOLE_ENTRIES_INDEX, JSTaggedValue(nod)); +} + +template +void TaggedHashTable::SetHashTableSize(const JSThread *thread, int size) +{ + Set(thread, SIZE_INDEX, JSTaggedValue(size)); +} + +template +void TaggedHashTable::GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const +{ + ASSERT_PRINT(offset + EntriesCount() <= static_cast(keyArray->GetLength()), + "keyArray size is not enough for dictionary"); + int arrayIndex = 0; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + keyArray->Set(thread, arrayIndex + offset, key); + arrayIndex++; + } + } +} + +// Find entry for key otherwise return -1. +template +int TaggedHashTable::FindEntry(const JSTaggedValue &key) +{ + size_t size = Size(); + int count = 1; + JSTaggedValue keyValue; + int hash = Derived::Hash(key); + + for (int entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) { + keyValue = GetKey(entry); + if (keyValue.IsHole()) { + continue; + } + if (keyValue.IsUndefined()) { + return -1; + } + if (Derived::IsMatch(key, keyValue)) { + return entry; + } + } + return -1; +} + +// static +template +int TaggedHashTable::RecalculateTableSize(int currentSize, int atLeastSize) +{ + // When the filled entries is greater than a quart of currentSize + // it need not to shrink + if (atLeastSize > (currentSize / 4)) { // 4 : quarter + return currentSize; + } + // Recalculate table size + int newSize = ComputeHashTableSize(atLeastSize); + ASSERT_PRINT(newSize > atLeastSize, "new size must greater than atLeastSize"); + // Don't go lower than room for MIN_SHRINK_SIZE elements. + if (newSize < MIN_SHRINK_SIZE) { + return currentSize; + } + return newSize; +} + +// static +template +Derived *TaggedHashTable::Shrink(const JSThread *thread, const JSHandle &table, int additionalSize) +{ + int newSize = RecalculateTableSize(table->Size(), table->EntriesCount() + additionalSize); + if (newSize == table->Size()) { + return *table; + } + + Derived *newTable = TaggedHashTable::Create(thread, newSize); + + table->Rehash(thread, newTable); + return newTable; +} + +template +bool TaggedHashTable::IsNeedGrowHashTable(int numOfAddEntries) +{ + int entriesCount = EntriesCount(); + int numOfDelEntries = HoleEntriesCount(); + int currentSize = Size(); + int numberFilled = entriesCount + numOfAddEntries; + // needn't to grow table: + // 1. after adding number entries, table have half free entries. + // 2. deleted entries are less than half of the free entries. + const int halfFree = 2; + if ((numberFilled < currentSize) && ((numOfDelEntries <= (currentSize - numberFilled) / halfFree))) { + int neededFree = numberFilled / halfFree; + if (numberFilled + neededFree <= currentSize) { + return false; + } + } + return true; +} + +template +void TaggedHashTable::AddElement(const JSThread *thread, int entry, const JSHandle &key, + const JSHandle &value) +{ + this->SetKey(thread, entry, key.GetTaggedValue()); + this->SetValue(thread, entry, value.GetTaggedValue()); + this->IncreaseEntries(thread); +} + +template +void TaggedHashTable::RemoveElement(const JSThread *thread, int entry) +{ + JSTaggedValue defaultValue(JSTaggedValue::Hole()); + this->SetKey(thread, entry, defaultValue); + this->SetValue(thread, entry, defaultValue); + this->IncreaseHoleEntriesCount(thread); +} + +template +Derived *TaggedHashTable::Insert(const JSThread *thread, JSHandle &table, + const JSHandle &key, const JSHandle &value) +{ + // Make sure the key object has an identity hash code. + int hash = Derived::Hash(key.GetTaggedValue()); + int entry = table->FindEntry(key.GetTaggedValue()); + if (entry != -1) { + table->SetValue(thread, entry, value.GetTaggedValue()); + return *table; + } + + Derived *newTable = GrowHashTable(thread, table); + newTable->AddElement(thread, newTable->FindInsertIndex(hash), key, value); + return newTable; +} + +template +Derived *TaggedHashTable::Remove(const JSThread *thread, JSHandle &table, + const JSHandle &key) +{ + int entry = table->FindEntry(key.GetTaggedValue()); + if (entry == -1) { + return *table; + } + + table->RemoveElement(thread, entry); + return Derived::Shrink(thread, table); +} + +template +void TaggedHashTable::Rehash(const JSThread *thread, Derived *newTable) +{ + if ((newTable == nullptr) || (newTable->Size() < EntriesCount())) { + return; + } + int currentSize = this->Size(); + // Rehash elements to new table + for (int i = 0; i < currentSize; i++) { + int fromIndex = Derived::GetKeyIndex(i); + JSTaggedValue k = this->GetKey(i); + if (!IsKey(k)) { + continue; + } + int hash = Derived::Hash(k); + int insertionIndex = Derived::GetKeyIndex(newTable->FindInsertIndex(hash)); + JSTaggedValue tv = Get(fromIndex); + newTable->Set(thread, insertionIndex, tv); + for (int j = 1; j < Derived::GetEntrySize(); j++) { + tv = Get(fromIndex + j); + newTable->Set(thread, insertionIndex + j, tv); + } + } + newTable->SetEntriesCount(thread, EntriesCount()); + newTable->SetHoleEntriesCount(thread, 0); +} + +// static +template +int TaggedHashTable::ComputeHashTableSize(uint32_t atLeastSize) +{ + // increase size for hash-collision + uint32_t rawSize = atLeastSize + (atLeastSize >> 1UL); + int newSize = static_cast(helpers::math::GetPowerOfTwoValue32(rawSize)); + return (newSize > MIN_SIZE) ? newSize : MIN_SIZE; +} + +template +Derived *TaggedHashTable::GrowHashTable(const JSThread *thread, const JSHandle &table, + int numOfAddedElements) +{ + if (!table->IsNeedGrowHashTable(numOfAddedElements)) { + return *table; + } + int newSize = ComputeHashTableSize(table->Size() + numOfAddedElements); + int length = Derived::GetEntryIndex(newSize); + auto newTable = static_cast(*thread->GetEcmaVM()->GetFactory()->NewDictionaryArray(length)); + newTable->SetHashTableSize(thread, newSize); + table->Rehash(thread, newTable); + return newTable; +} + +template +Derived *TaggedHashTable::Create(const JSThread *thread, int entriesCount) +{ + ASSERT_PRINT((entriesCount > 0), "the size must be greater than zero"); + auto size = static_cast(entriesCount); + ASSERT_PRINT(helpers::math::IsPowerOfTwo(static_cast(entriesCount)), "the size must be power of two"); + + int length = Derived::GetEntryIndex(entriesCount); + + auto table = static_cast(*thread->GetEcmaVM()->GetFactory()->NewDictionaryArray(length)); + table->SetEntriesCount(thread, 0); + table->SetHoleEntriesCount(thread, 0); + table->SetHashTableSize(thread, size); + return table; +} + +template +void TaggedHashTable::SetKey(const JSThread *thread, int entry, const JSTaggedValue &key) +{ + int index = Derived::GetKeyIndex(entry); + if (UNLIKELY(index < 0 || index > static_cast(GetLength()))) { + return; + } + Set(thread, index, key); +} + +template +JSTaggedValue TaggedHashTable::GetKey(int entry) const +{ + int index = Derived::GetKeyIndex(entry); + if (UNLIKELY((index < 0 || index > static_cast(GetLength())))) { + return JSTaggedValue::Undefined(); + } + return Get(index); +} + +template +void TaggedHashTable::SetValue(const JSThread *thread, int entry, const JSTaggedValue &value) +{ + int index = Derived::GetValueIndex(entry); + if (UNLIKELY((index < 0 || index > static_cast(GetLength())))) { + return; + } + Set(thread, index, value); +} + +template +JSTaggedValue TaggedHashTable::GetValue(int entry) const +{ + int index = Derived::GetValueIndex(entry); + if (UNLIKELY((index < 0 || index > static_cast(GetLength())))) { + return JSTaggedValue::Undefined(); + } + return Get(index); +} + +template +int TaggedHashTable::FindInsertIndex(int hash) +{ + int size = Size(); + int count = 1; + // GrowHashTable will guarantee the hash table is never full. + for (int entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) { + if (!IsKey(GetKey(entry))) { + return entry; + } + } +} + +template +Derived *OrderTaggedHashTable::Create(const JSThread *thread, int numberOfElements) +{ + Derived *dict = HashTableT::Create(thread, numberOfElements); + dict->SetNextEnumerationIndex(thread, PropertyAttributes::INTIAL_PROPERTY_INDEX); + return dict; +} + +template +Derived *OrderTaggedHashTable::PutIfAbsent(const JSThread *thread, const JSHandle &table, + const JSHandle &key, + const JSHandle &value, + const PropertyAttributes &metaData) +{ + int hash = Derived::Hash(key.GetTaggedValue()); + + /* no need to add key if exist */ + int entry = table->FindEntry(key.GetTaggedValue()); + if (entry != -1) { + return *table; + } + int enumIndex = table->NextEnumerationIndex(thread); + PropertyAttributes attr(metaData); + attr.SetDictionaryOrder(enumIndex); + // Check whether the table should be growed. + Derived *newTable = HashTableT::GrowHashTable(thread, table); + + // Compute the key object. + entry = newTable->FindInsertIndex(hash); + newTable->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr); + + newTable->IncreaseEntries(thread); + newTable->SetNextEnumerationIndex(thread, enumIndex + 1); + return newTable; +} + +template +Derived *OrderTaggedHashTable::Put(const JSThread *thread, const JSHandle &table, + const JSHandle &key, const JSHandle &value, + const PropertyAttributes &metaData) +{ + int hash = Derived::Hash(key.GetTaggedValue()); + int enumIndex = table->NextEnumerationIndex(thread); + PropertyAttributes attr(metaData); + attr.SetDictionaryOrder(enumIndex); + int entry = table->FindEntry(key.GetTaggedValue()); + if (entry != -1) { + table->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr); + return *table; + } + // Check whether the table should be extended. + Derived *newTable = HashTableT::GrowHashTable(thread, table); + + // Compute the key object. + entry = newTable->FindInsertIndex(hash); + newTable->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr); + + newTable->IncreaseEntries(thread); + newTable->SetNextEnumerationIndex(thread, enumIndex + 1); + return newTable; +} + +template +void TaggedHashTable::GetAllKeysIntoVector(const JSThread *thread, std::vector &vector) const +{ + int capacity = Size(); + for (int hashIndex = 0; hashIndex < capacity; hashIndex++) { + JSTaggedValue key = GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + vector.push_back(key); + } + } +} + +template +Derived *OrderTaggedHashTable::Remove(const JSThread *thread, const JSHandle &table, int entry) +{ + if (!(table->IsKey(table->GetKey(entry)))) { + return *table; + } + table->ClearEntry(thread, entry); + table->IncreaseHoleEntriesCount(thread); + return Shrink(thread, table); +} + +template +int OrderTaggedHashTable::NextEnumerationIndex(const JSThread *thread) +{ + int index = GetNextEnumerationIndex(); + auto table = Derived::Cast(this); + + if (!PropertyAttributes::IsValidIndex(index)) { + std::vector indexOrder = GetEnumerationOrder(); + int length = indexOrder.size(); + for (int i = 0; i < length; i++) { + int oldIndex = indexOrder[i]; + int enumIndex = PropertyAttributes::INTIAL_PROPERTY_INDEX + i; + PropertyAttributes attr = table->GetAttributes(oldIndex); + attr.SetDictionaryOrder(enumIndex); + table->SetAttributes(thread, oldIndex, attr); + } + index = PropertyAttributes::INTIAL_PROPERTY_INDEX + length; + } + return index; +} + +template +std::vector OrderTaggedHashTable::GetEnumerationOrder() +{ + std::vector result; + auto table = Derived::Cast(this); + int size = table->Size(); + for (int i = 0; i < size; i++) { + if (table->IsKey(table->GetKey(i))) { + result.push_back(i); + } + } + std::sort(result.begin(), result.end(), [table](int a, int b) { + PropertyAttributes attrA = table->GetAttributes(a); + PropertyAttributes attrB = table->GetAttributes(b); + return attrA.GetDictionaryOrder() < attrB.GetDictionaryOrder(); + }); + return result; +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_TAGGED_HASH_TABLE_INL_H diff --git a/ecmascript/tagged_hash_table.h b/ecmascript/tagged_hash_table.h new file mode 100644 index 0000000000000000000000000000000000000000..4611ba6a775b29ddea03352ecee70df6502f1b75 --- /dev/null +++ b/ecmascript/tagged_hash_table.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TAGGED_HASH_TABLE_H +#define PANDA_RUNTIME_ECMASCRIPT_TAGGED_HASH_TABLE_H + +#include + +#include "js_handle.h" +#include "tagged_array.h" + +namespace panda::ecmascript { +template +class TaggedHashTable : public TaggedArray { +public: + inline int EntriesCount() const; + + inline int HoleEntriesCount() const; + + inline int Size() const; + + inline void IncreaseEntries(const JSThread *thread); + + inline void IncreaseHoleEntriesCount(const JSThread *thread, int number = 1); + + inline static int ComputeHashTableSize(uint32_t atLeastSize); + + static Derived *GrowHashTable(const JSThread *thread, const JSHandle &table, int numOfAddedElements = 1); + + static Derived *Create(const JSThread *thread, int numberOfElements); + + static Derived *Insert(const JSThread *thread, JSHandle &table, const JSHandle &key, + const JSHandle &value); + + static Derived *Remove(const JSThread *thread, JSHandle &table, const JSHandle &key); + + inline static int RecalculateTableSize(int currentSize, int atLeastSize); + + inline static Derived *Shrink(const JSThread *thread, const JSHandle &table, int additionalSize); + + bool IsNeedGrowHashTable(int numOfAddEntries); + + JSTaggedValue GetKey(int entry) const; + + JSTaggedValue GetValue(int entry) const; + + inline void GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const; + + inline void GetAllKeysIntoVector(const JSThread *thread, std::vector &vector) const; + + inline void Swap(const JSThread *thread, int src, int dst); + + // Find entry for key otherwise return -1. + inline int FindEntry(const JSTaggedValue &key); + + inline int FindInsertIndex(int hash); + + inline void SetKey(const JSThread *thread, int entry, const JSTaggedValue &key); + + inline void SetValue(const JSThread *thread, int entry, const JSTaggedValue &value); + + static constexpr int MIN_SHRINK_SIZE = 16; + static constexpr int MIN_SIZE = 4; + static constexpr int NUMBER_OF_ENTRIES_INDEX = 0; + static constexpr int NUMBER_OF_HOLE_ENTRIES_INDEX = 1; + static constexpr int SIZE_INDEX = 2; + static constexpr int TABLE_HEADER_SIZE = 3; + +protected: + inline bool IsKey(const JSTaggedValue &key) const + { + return !key.IsHole() && !key.IsUndefined(); + }; + + inline static uint32_t GetFirstPosition(uint32_t hash, uint32_t size) + { + return hash & (size - 1); + } + + inline static uint32_t GetNextPosition(uint32_t last, uint32_t number, uint32_t size) + { + return (last + (number * (number + 1)) / 2) & (size - 1); // 2 : half + } + + inline void SetEntriesCount(const JSThread *thread, int noe); + + inline void SetHoleEntriesCount(const JSThread *thread, int nod); + + // Sets the size of the hash table. + inline void SetHashTableSize(const JSThread *thread, int size); + + inline static int GetHeadSizeOfTable(); + inline static int GetEntrySize(); + inline static int GetKeyOffset(); + inline static int GetValueOffset(); + + inline void AddElement(const JSThread *thread, int entry, const JSHandle &key, + const JSHandle &value); + + inline void RemoveElement(const JSThread *thread, int entry); + + // Rehash element to new_table + void Rehash(const JSThread *thread, Derived *newTable); +}; + +template +class OrderTaggedHashTable : public TaggedHashTable { +public: + using HashTableT = TaggedHashTable; + static Derived *Cast(TaggedObject *object) + { + return reinterpret_cast(object); + } + // Attempt to shrink the table after deletion of key. + static Derived *Shrink(const JSThread *thread, const JSHandle &table) + { + int index = table->GetNextEnumerationIndex(); + Derived *newTable = HashTableT::Shrink(thread, table, 0); + newTable->SetNextEnumerationIndex(thread, index); + return newTable; + } + + static Derived *Create(const JSThread *thread, int numberOfElements = DEFAULT_ELEMENTS_NUMBER); + static Derived *PutIfAbsent(const JSThread *thread, const JSHandle &table, + const JSHandle &key, const JSHandle &value, + const PropertyAttributes &metaData); + static Derived *Put(const JSThread *thread, const JSHandle &table, const JSHandle &key, + const JSHandle &value, const PropertyAttributes &metaData); + static Derived *Remove(const JSThread *thread, const JSHandle &table, int entry); + + inline void SetNextEnumerationIndex(const JSThread *thread, int index) + { + HashTableT::Set(thread, NEXT_ENUMERATION_INDEX, JSTaggedValue(index)); + } + inline int GetNextEnumerationIndex() const + { + return HashTableT::Get(NEXT_ENUMERATION_INDEX).GetInt(); + } + + inline int NextEnumerationIndex(const JSThread *thread); + + inline std::vector GetEnumerationOrder(); + + static const int NEXT_ENUMERATION_INDEX = HashTableT::SIZE_INDEX + 1; + static const int DEFAULT_ELEMENTS_NUMBER = 128; + static constexpr int TABLE_HEADER_SIZE = 4; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_NEW_HASH_TABLE_H \ No newline at end of file diff --git a/ecmascript/tagged_queue-inl.h b/ecmascript/tagged_queue-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..e655e40538ff0dd70f401309a5ec1120aaff14ed --- /dev/null +++ b/ecmascript/tagged_queue-inl.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TAGGED_QUEUE_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_TAGGED_QUEUE_INL_H + +#include "ecmascript/tagged_queue.h" + +namespace panda::ecmascript { +inline TaggedQueue *TaggedQueue::Create(JSThread *thread, array_size_t capacity, JSTaggedValue initVal) +{ + array_size_t length = QueueToArrayIndex(capacity); + + auto queue = TaggedQueue::Cast(*thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, initVal)); + queue->SetStart(thread, JSTaggedValue(0)); // equal to 0 when add 1. + queue->SetEnd(thread, JSTaggedValue(0)); + queue->SetCapacity(thread, JSTaggedValue(capacity)); + return queue; +} + +inline JSTaggedValue TaggedQueue::Pop(JSThread *thread) +{ + if (Empty()) { + return JSTaggedValue::Hole(); + } + + array_size_t start = GetStart().GetArrayLength(); + JSTaggedValue value = Get(start); + + array_size_t capacity = GetCapacity().GetArrayLength(); + SetStart(thread, JSTaggedValue((start + 1) % capacity)); + return value; +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_TAGGED_QUEUE_INL_H \ No newline at end of file diff --git a/ecmascript/tagged_queue.h b/ecmascript/tagged_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..cdf400fde5393d8ebfa702a0c9295dd004fc265d --- /dev/null +++ b/ecmascript/tagged_queue.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TAGGED_QUEUE_H +#define PANDA_RUNTIME_ECMASCRIPT_TAGGED_QUEUE_H + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/tagged_array.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript { +class TaggedQueue : public TaggedArray { +public: + static TaggedQueue *Cast(ObjectHeader *object) + { + return reinterpret_cast(object); + } + + JSTaggedValue Pop(JSThread *thread); + static TaggedQueue *Push(const JSThread *thread, const JSHandle &queue, + const JSHandle &value) + { + array_size_t capacity = queue->GetCapacity().GetArrayLength(); + if (capacity == 0) { + // If there is no capacity, directly create a queue whose capacity is MIN_CAPACITY. Add elements. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newQueue = factory->NewTaggedQueue(MIN_CAPACITY); + newQueue->Set(thread, 0, value.GetTaggedValue()); + newQueue->SetCapacity(thread, JSTaggedValue(MIN_CAPACITY)); + newQueue->SetStart(thread, JSTaggedValue(0)); + newQueue->SetEnd(thread, JSTaggedValue(1)); + return *newQueue; + } + + array_size_t start = queue->GetStart().GetArrayLength(); + array_size_t end = queue->GetEnd().GetArrayLength(); + array_size_t size = queue->Size(); + if ((end + 1) % capacity == start) { + // The original queue is full and needs to be expanded. + if (capacity == MAX_QUEUE_INDEX) { + // can't grow anymore + return *queue; + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // Grow Array for 1.5 times + array_size_t newCapacity = capacity + (capacity >> 1U); + newCapacity = newCapacity < capacity ? MAX_QUEUE_INDEX : newCapacity; + JSHandle newQueue = factory->NewTaggedQueue(newCapacity); + array_size_t newEnd = 0; + for (array_size_t i = start; newEnd < size; i = (i + 1) % capacity) { + newQueue->Set(thread, newEnd, queue->Get(i)); + newEnd++; + } + + newQueue->SetCapacity(thread, JSTaggedValue(newCapacity)); + newQueue->SetStart(thread, JSTaggedValue(0)); + newQueue->Set(thread, newEnd++, value.GetTaggedValue()); + newQueue->SetEnd(thread, JSTaggedValue(newEnd)); + return *newQueue; + } + queue->Set(thread, end, value.GetTaggedValue()); + queue->SetEnd(thread, JSTaggedValue((end + 1) % capacity)); + return *queue; + } + + // Only for fixed-length queue, without grow capacity + static inline void PushFixedQueue(const JSThread *thread, const JSHandle &queue, + const JSHandle &value) + { + array_size_t end = queue->GetEnd().GetArrayLength(); + array_size_t capacity = queue->GetCapacity().GetArrayLength(); + queue->Set(thread, end, value.GetTaggedValue()); + queue->SetEnd(thread, JSTaggedValue((end + 1) % capacity)); + } + + inline bool Empty() + { + return GetStart() == GetEnd(); + } + + inline JSTaggedValue Front() + { + if (Empty()) { + return JSTaggedValue::Hole(); + } + array_size_t start = GetStart().GetArrayLength(); + return JSTaggedValue(Get(start)); + } + + inline JSTaggedValue Back() + { + if (Empty()) { + return JSTaggedValue::Hole(); + } + return JSTaggedValue(Get(GetEnd().GetArrayLength() - 1)); + } + + inline array_size_t Size() + { + array_size_t capacity = GetCapacity().GetArrayLength(); + if (capacity == 0) { + return 0; + } + array_size_t end = GetEnd().GetArrayLength(); + array_size_t start = GetStart().GetArrayLength(); + return (end - start + capacity) % capacity; + } + + inline JSTaggedValue Get(array_size_t index) const + { + return TaggedArray::Get(QueueToArrayIndex(index)); + } + + inline void Set(const JSThread *thread, array_size_t index, JSTaggedValue value) + { + return TaggedArray::Set(thread, QueueToArrayIndex(index), value); + } + + static const array_size_t MIN_CAPACITY = 2; + static const array_size_t CAPACITY_INDEX = 0; + static const array_size_t START_INDEX = 1; + static const array_size_t END_INDEX = 2; + static const array_size_t ELEMENTS_START_INDEX = 3; + static const array_size_t MAX_QUEUE_INDEX = TaggedArray::MAX_ARRAY_INDEX - ELEMENTS_START_INDEX; + +private: + friend class ObjectFactory; + + inline static constexpr array_size_t QueueToArrayIndex(array_size_t index) + { + return index + ELEMENTS_START_INDEX; + } + + inline void SetCapacity(const JSThread *thread, JSTaggedValue capacity) + { + TaggedArray::Set(thread, CAPACITY_INDEX, capacity); + } + + inline JSTaggedValue GetCapacity() const + { + return TaggedArray::Get(CAPACITY_INDEX); + } + + inline void SetStart(const JSThread *thread, JSTaggedValue start) + { + TaggedArray::Set(thread, START_INDEX, start); + } + + inline JSTaggedValue GetStart() const + { + return TaggedArray::Get(START_INDEX); + } + + inline void SetEnd(const JSThread *thread, JSTaggedValue end) + { + TaggedArray::Set(thread, END_INDEX, end); + } + + inline JSTaggedValue GetEnd() const + { + return TaggedArray::Get(END_INDEX); + } + + static TaggedQueue *Create(JSThread *thread, array_size_t capacity, JSTaggedValue initVal = JSTaggedValue::Hole()); +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_TAGGED_QUEUE_H \ No newline at end of file diff --git a/ecmascript/template_map.h b/ecmascript/template_map.h new file mode 100644 index 0000000000000000000000000000000000000000..355c03404a855eb0a4e07f1f0da54416749460af --- /dev/null +++ b/ecmascript/template_map.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TEMPLATE_MAP_H +#define PANDA_RUNTIME_ECMASCRIPT_TEMPLATE_MAP_H + +#include "ecmascript/tagged_hash_table-inl.h" +#include "ecmascript/js_array.h" + +namespace panda::ecmascript { +class TemplateMap : public TaggedHashTable { +public: + using HashTable = TaggedHashTable; + static inline bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &other) + { + return key == other; + } + static inline int Hash(const JSTaggedValue &obj) + { + ASSERT(obj.IsJSArray()); + JSArray *array = JSArray::Cast(obj.GetHeapObject()); + uint32_t len = array->GetArrayLength(); + return len; + } + inline static int GetKeyIndex(int entry) + { + return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + static TemplateMap *Cast(ObjectHeader *object) + { + return reinterpret_cast(object); + } + static const int DEFAULT_ELEMENTS_NUMBER = 16; + static TemplateMap *Create(JSThread *thread, int numberOfElements = DEFAULT_ELEMENTS_NUMBER) + { + return HashTable::Create(thread, numberOfElements); + } + static const int ENTRY_SIZE = 2; + static const int ENTRY_KEY_INDEX = 0; + static const int ENTRY_VALUE_INDEX = 1; +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_TEMPLATE_MAP_H \ No newline at end of file diff --git a/ecmascript/template_string.cpp b/ecmascript/template_string.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4073a96f52dbdc6aad52c1b4fc68f5b372512b12 --- /dev/null +++ b/ecmascript/template_string.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/template_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/template_map.h" + +namespace panda::ecmascript { +JSHandle TemplateString::GetTemplateObject(JSThread *thread, JSHandle templateLiteral) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle rawStringsTag = JSObject::GetProperty(thread, templateLiteral, 0).GetValue(); + JSHandle templateMapTag = env->GetTemplateMap(); + JSHandle templateMap(templateMapTag); + int32_t element = templateMap->FindEntry(rawStringsTag.GetTaggedValue()); + if (element != -1) { + return JSHandle(thread, templateMap->GetValue(element)); + } + JSHandle cookedStringsTag = JSObject::GetProperty(thread, templateLiteral, 1).GetValue(); + JSHandle cookedStrings(cookedStringsTag); + int32_t count = cookedStrings->GetArrayLength(); + auto countNum = JSTaggedNumber(count); + JSHandle templateArr = JSArray::ArrayCreate(thread, countNum); + JSHandle rawArr = JSArray::ArrayCreate(thread, countNum); + JSHandle templateObj(templateArr); + JSHandle rawObj(rawArr); + for (int32_t i = 0; i < count; i++) { + JSHandle cookedValue = JSObject::GetProperty(thread, cookedStringsTag, i).GetValue(); + PropertyDescriptor descCooked(thread, cookedValue, true, false, false); + JSArray::DefineOwnProperty(thread, templateObj, i, descCooked); + JSHandle rawValue = JSObject::GetProperty(thread, rawStringsTag, i).GetValue(); + PropertyDescriptor descRaw(thread, rawValue, true, false, false); + JSArray::DefineOwnProperty(thread, rawObj, i, descRaw); + } + JSObject::SetIntegrityLevel(thread, rawObj, IntegrityLevel::FROZEN); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle raw(factory->NewFromString("raw")); + PropertyDescriptor desc(thread, rawArr, false, false, false); + JSArray::DefineOwnProperty(thread, templateObj, raw, desc); + JSObject::SetIntegrityLevel(thread, templateObj, IntegrityLevel::FROZEN); + TemplateMap::Insert(thread, templateMap, rawStringsTag, templateArr); + env->SetTemplateMap(thread, templateMap.GetTaggedValue()); + return templateArr; +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/template_string.h b/ecmascript/template_string.h new file mode 100644 index 0000000000000000000000000000000000000000..080291e7ef43dce7116f617f99df78d5ae48f8ae --- /dev/null +++ b/ecmascript/template_string.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TEMPLATE_STRING_H +#define PANDA_RUNTIME_ECMASCRIPT_TEMPLATE_STRING_H + +#include "ecmascript/js_array.h" + +namespace panda::ecmascript { +class TemplateString { +public: + static JSHandle GetTemplateObject(JSThread *thread, JSHandle templateLiteral); +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_TEMPLATE_STRING_H \ No newline at end of file diff --git a/ecmascript/tests/BUILD.gn b/ecmascript/tests/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..cd2527e89b8aa9ef019cede724edf3d8c2e0dfb3 --- /dev/null +++ b/ecmascript/tests/BUILD.gn @@ -0,0 +1,755 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//ark/js_runtime/test/test_helper.gni") +import("//build/test.gni") + +module_output_path = "ark/js_runtime" + +host_unittest_action("AssertScopeTest") { + module_out_path = module_output_path + + sources = [ + # test file + "assert_scope_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("BuiltinsTest") { + module_out_path = module_output_path + + sources = [ + # test file + "builtins_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("DumpTest") { + module_out_path = module_output_path + + sources = [ + # test file + "dump_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("GlueRegsTest") { + module_out_path = module_output_path + + sources = [ + # test file + "glue_regs_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsArrayTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_array_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsDateTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_date_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsForinIteratorTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_forin_iterator_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsFunctionTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_function_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsHandleTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_handle_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsIteratorTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_iterator_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsMapTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_map_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsObjectTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_object_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsPrimitiveRefTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_primitive_ref_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsPromiseTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_promise_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsProxyTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_proxy_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsSetTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_set_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsSymbolTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_symbol_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsTaggedQueueTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_tagged_queue_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsVerificationTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_verification_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JargeObjectTest") { + module_out_path = module_output_path + + sources = [ + # test file + "large_object_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JexicalEnvTest") { + module_out_path = module_output_path + + sources = [ + # test file + "lexical_env_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JinkedHashTableTest") { + module_out_path = module_output_path + + sources = [ + # test file + "linked_hash_table_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("NameDictionaryTest") { + module_out_path = module_output_path + + sources = [ + # test file + "name_dictionary_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("NativePointerTest") { + module_out_path = module_output_path + + sources = [ + # test file + "native_pointer_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("ObjectFactoryTest") { + module_out_path = module_output_path + + sources = [ + # test file + "object_factory_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("TaggedValueTest") { + module_out_path = module_output_path + + sources = [ + # test file + "tagged_value_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("WeakRefGenGcTest") { + module_out_path = module_output_path + + sources = [ + # test file + "weak_ref_gen_gc_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("WeakRefStwGcTest") { + module_out_path = module_output_path + + sources = [ + # test file + "weak_ref_stw_gc_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("JsSerializerTest") { + module_out_path = module_output_path + + sources = [ + # test file + "js_serializer_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("GcTest") { + module_out_path = module_output_path + + sources = [ + # test file + "gc_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + sdk_libc_secshared_dep, + ] +} + +group("unittest") { + testonly = true + + # deps file + deps = [ + ":AssertScopeTest", + ":BuiltinsTest", + ":DumpTest", + ":GcTest", + ":GlueRegsTest", + ":JargeObjectTest", + ":JexicalEnvTest", + ":JinkedHashTableTest", + ":JsArrayTest", + ":JsDateTest", + ":JsForinIteratorTest", + ":JsFunctionTest", + ":JsHandleTest", + ":JsIteratorTest", + ":JsMapTest", + ":JsObjectTest", + ":JsPrimitiveRefTest", + ":JsPromiseTest", + ":JsProxyTest", + + # ":JsSerializerTest", + ":JsSetTest", + ":JsSymbolTest", + ":JsTaggedQueueTest", + ":JsVerificationTest", + ":NameDictionaryTest", + ":NativePointerTest", + ":ObjectFactoryTest", + ":TaggedValueTest", + ":WeakRefGenGcTest", + ":WeakRefStwGcTest", + ] +} + +group("host_unittest") { + testonly = true + + # deps file + deps = [ + ":AssertScopeTestAction(${host_toolchain})", + ":BuiltinsTestAction(${host_toolchain})", + ":DumpTestAction(${host_toolchain})", + ":GcTestAction(${host_toolchain})", + ":GlueRegsTestAction(${host_toolchain})", + ":JargeObjectTestAction(${host_toolchain})", + ":JexicalEnvTestAction(${host_toolchain})", + ":JinkedHashTableTestAction(${host_toolchain})", + ":JsArrayTestAction(${host_toolchain})", + ":JsDateTestAction(${host_toolchain})", + ":JsForinIteratorTestAction(${host_toolchain})", + ":JsFunctionTestAction(${host_toolchain})", + ":JsHandleTestAction(${host_toolchain})", + ":JsIteratorTestAction(${host_toolchain})", + ":JsMapTestAction(${host_toolchain})", + ":JsObjectTestAction(${host_toolchain})", + ":JsPrimitiveRefTestAction(${host_toolchain})", + ":JsPromiseTestAction(${host_toolchain})", + ":JsProxyTestAction(${host_toolchain})", + + # ":JsSerializerTestAction(${host_toolchain})", + ":JsSetTestAction(${host_toolchain})", + ":JsSymbolTestAction(${host_toolchain})", + ":JsTaggedQueueTestAction(${host_toolchain})", + ":JsVerificationTestAction(${host_toolchain})", + ":NameDictionaryTestAction(${host_toolchain})", + ":NativePointerTestAction(${host_toolchain})", + ":ObjectFactoryTestAction(${host_toolchain})", + ":TaggedValueTestAction(${host_toolchain})", + ":WeakRefGenGcTestAction(${host_toolchain})", + ":WeakRefStwGcTestAction(${host_toolchain})", + ] +} diff --git a/ecmascript/tests/assert_scope_test.cpp b/ecmascript/tests/assert_scope_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f580812901b80f996268c3f3b729a62d07d391ca --- /dev/null +++ b/ecmascript/tests/assert_scope_test.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/mem/assert_scope-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class AssertScopeTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override {} + + void TearDown() override {} +}; + +HWTEST_F_L0(AssertScopeTest, AssertScopeChecker) +{ +#if !defined(NDEBUG) + { + EXPECT_TRUE(AllowHeapAlloc::IsAllowed()); + EXPECT_TRUE(AllowGarbageCollection::IsAllowed()); + [[maybe_unused]] DisallowGarbageCollection noGc; // set dis-allow gc + { + [[maybe_unused]] AllowGarbageCollection allowGc; // set allow gc + EXPECT_TRUE(AllowGarbageCollection::IsAllowed()); + + [[maybe_unused]] DisAllowHeapAlloc noHeapAlloc; // set dis-allow alloc + { + [[maybe_unused]] AllowHeapAlloc heapAlloc; // set allow alloc + EXPECT_TRUE(AllowHeapAlloc::IsAllowed()); + } + EXPECT_FALSE(AllowHeapAlloc::IsAllowed()); + } + // allow alloc + EXPECT_TRUE(AllowHeapAlloc::IsAllowed()); + // dis-allow gc + EXPECT_FALSE(AllowGarbageCollection::IsAllowed()); + } + // allow gc + EXPECT_TRUE(AllowGarbageCollection::IsAllowed()); +#endif + EXPECT_TRUE(AllowHeapAlloc::IsAllowed()); + EXPECT_TRUE(AllowGarbageCollection::IsAllowed()); +} +} // namespace panda::test diff --git a/ecmascript/tests/builtins_test.cpp b/ecmascript/tests/builtins_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..db5dd7a209787bd7c9759cf9ac0a052e85d3586a --- /dev/null +++ b/ecmascript/tests/builtins_test.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tests/test_helper.h" + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/builtins.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_thread.h" +#include "thread_manager.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; + +namespace panda::test { +class BuiltinsTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(BuiltinsTest, ObjectInit) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle objectFunction(env->GetObjectFunction()); + ASSERT_NE(*objectFunction, nullptr); +} + +HWTEST_F_L0(BuiltinsTest, FunctionInit) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle functionFunction(env->GetFunctionFunction()); + ASSERT_NE(*functionFunction, nullptr); +} + +HWTEST_F_L0(BuiltinsTest, NumberInit) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberFunction(env->GetNumberFunction()); + ASSERT_NE(*numberFunction, nullptr); +} + +HWTEST_F_L0(BuiltinsTest, SetInit) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle setFunction(env->GetBuiltinsSetFunction()); + ASSERT_NE(*setFunction, nullptr); +} + +HWTEST_F_L0(BuiltinsTest, MapInit) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle mapFunction(env->GetBuiltinsMapFunction()); + ASSERT_NE(*mapFunction, nullptr); +} + +HWTEST_F_L0(BuiltinsTest, StrictModeForbiddenAccess) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle function = factory->NewJSFunction(env, static_cast(nullptr)); + + JSHandle callerKey(factory->NewFromString("caller")); + JSHandle argumentsKey(factory->NewFromString("arguments")); + + JSObject::GetProperty(thread, JSHandle(function), callerKey); + ASSERT_EQ(thread->HasPendingException(), true); + + JSObject::GetProperty(thread, JSHandle(function), argumentsKey); + ASSERT_EQ(thread->HasPendingException(), true); +} +} // namespace panda::test diff --git a/ecmascript/tests/dump_test.cpp b/ecmascript/tests/dump_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b7142c434353fb27693853ae5d8c74bd76a9500f --- /dev/null +++ b/ecmascript/tests/dump_test.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_date.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_map.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_regexp.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/linked_hash_table-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class EcmaDumpTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +#ifndef NDEBUG +HWTEST_F_L0(EcmaDumpTest, Dump) +{ + auto jsThread = thread; + JSTaggedValue value1(100); + value1.Dump(jsThread); + + JSTaggedValue value2(100.0); + JSTaggedValue::DumpVal(jsThread, value2.GetRawData()); + + JSTaggedValue::Undefined().Dump(jsThread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + env.Dump(jsThread); + + JSHandle objFunc(env->GetObjectFunction()); + objFunc.Dump(jsThread); +} +#endif // #ifndef NDEBUG +} // namespace panda::test diff --git a/ecmascript/tests/gc_test.cpp b/ecmascript/tests/gc_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bece8eee7f97c335f28b1cf6f6f3c113141c20a8 --- /dev/null +++ b/ecmascript/tests/gc_test.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/mem/compress_collector.h" +#include "ecmascript/mem/semi_space_collector.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +namespace panda::test { +class GCTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + RuntimeOptions options; + options.SetShouldLoadBootPandaFiles(false); + options.SetShouldInitializeIntrinsics(false); + options.SetBootClassSpaces({"ecmascript"}); + options.SetRuntimeType("ecmascript"); + options.SetEnableParalledYoungGc(false); + static EcmaLanguageContext lcEcma; + [[maybe_unused]] bool success = Runtime::Create(options, {&lcEcma}); + ASSERT_TRUE(success) << "Cannot create Runtime"; + instance = Runtime::GetCurrent()->GetPandaVM(); + ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM"; + thread = EcmaVM::Cast(instance)->GetJSThread(); + scope = new EcmaHandleScope(thread); + thread->SetIsEcmaInterpreter(true); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(GCTest, CompressGCOne) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto heap = thread->GetEcmaVM()->GetHeap(); + auto compressGc = heap->GetCompressCollector(); + compressGc->RunPhases(); + auto oldSizebase = heap->GetOldSpace()->GetCommittedSize(); + { + [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); + for (int i = 0; i < 1024; i++) { + factory->NewTaggedArray(512, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); + } + auto oldSizeBefore = heap->GetOldSpace()->GetCommittedSize(); + EXPECT_TRUE(oldSizeBefore > oldSizebase); + } + compressGc->RunPhases(); + auto oldSizeAfter = heap->GetOldSpace()->GetCommittedSize(); + EXPECT_EQ(oldSizebase, oldSizeAfter); +} +} // namespace panda::test diff --git a/ecmascript/tests/glue_regs_test.cpp b/ecmascript/tests/glue_regs_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fe1e1794e1259fc91ea31fd10459b48ed0aeadb3 --- /dev/null +++ b/ecmascript/tests/glue_regs_test.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/builtins.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/global_env_constants-inl.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/tests/test_helper.h" +#include "thread_manager.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; + +namespace panda::test { +class GlueRegsTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(GlueRegsTest, ConstantClassTest) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ASSERT_NE(globalConst, nullptr); + + const JSTaggedValue *address = globalConst->BeginSlot(); + while (address < globalConst->EndSlot()) { + EXPECT_TRUE(!(*address).IsNull()); // Visit barely + address += sizeof(JSTaggedValue); + } +} + +HWTEST_F_L0(GlueRegsTest, ConstantSpecialTest) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ASSERT_NE(globalConst, nullptr); + + EXPECT_TRUE(globalConst->GetUndefined().IsUndefined()); + EXPECT_TRUE(globalConst->GetHandledUndefined()->IsUndefined()); + EXPECT_TRUE(globalConst->GetNull().IsNull()); + EXPECT_TRUE(globalConst->GetHandledNull()->IsNull()); + EXPECT_TRUE(globalConst->GetEmptyString().IsString()); + EXPECT_TRUE(globalConst->GetHandledEmptyString()->IsString()); +} + +HWTEST_F_L0(GlueRegsTest, ConstantStringTest) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ASSERT_NE(globalConst, nullptr); + +#define CONSTANT_STRING_ITERATOR(Type, Name, Index, Desc) \ + Type Name##value = globalConst->Get##Name(); \ + EXPECT_TRUE(!Name##value.IsNull()); \ + JSHandle Name##handledValue = globalConst->GetHandled##Name(); \ + EXPECT_TRUE(!Name##handledValue->IsNull()); + GLOBAL_ENV_CONSTANT_CONSTANT(CONSTANT_STRING_ITERATOR) +#undef CONSTANT_STRING_ITERATOR +} + +HWTEST_F_L0(GlueRegsTest, ConstantAccessorTest) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ASSERT_NE(globalConst, nullptr); + +#define CONSTANT_ACCESSOR_ITERATOR(Type, Name, Index, Desc) \ + Type Name##value = globalConst->Get##Name(); \ + EXPECT_TRUE(!Name##value.IsNull()); \ + JSHandle Name##handledValue = globalConst->GetHandled##Name(); \ + EXPECT_TRUE(!Name##handledValue->IsNull()); + GLOBAL_ENV_CONSTANT_ACCESSOR(CONSTANT_ACCESSOR_ITERATOR) +#undef CONSTANT_ACCESSOR_ITERATOR +} +} // namespace panda::test \ No newline at end of file diff --git a/ecmascript/tests/js_array_test.cpp b/ecmascript/tests/js_array_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..07beb38389eecc1a71f619c86120997289ea91cf --- /dev/null +++ b/ecmascript/tests/js_array_test.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JSArrayTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(JSArrayTest, ArrayCreate) +{ + JSHandle lengthKeyHandle(thread->GlobalConstants()->GetHandledLengthString()); + JSArray *arr = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, obj, lengthKeyHandle).GetValue()->GetInt(), 0); + + JSArray *arr2 = JSArray::ArrayCreate(thread, JSTaggedNumber(10)).GetObject(); + EXPECT_TRUE(arr2 != nullptr); + JSHandle obj2(thread, arr2); + EXPECT_EQ(JSArray::GetProperty(thread, obj2, lengthKeyHandle).GetValue()->GetInt(), 10); +} + +HWTEST_F_L0(JSArrayTest, ArraySpeciesCreate) +{ + JSHandle lengthKeyHandle(thread->GlobalConstants()->GetHandledLengthString()); + JSArray *arr = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSArray *arr2 = JSArray::Cast(JSArray::ArraySpeciesCreate(thread, obj, JSTaggedNumber(10)).GetTaggedObject()); + EXPECT_TRUE(arr2 != nullptr); + JSHandle obj2(thread, arr2); + EXPECT_EQ(JSArray::GetProperty(thread, obj2, lengthKeyHandle).GetValue()->GetInt(), 10); +} + +HWTEST_F_L0(JSArrayTest, DefineOwnProperty) +{ + auto ecmaVM = thread->GetEcmaVM(); + auto factory = ecmaVM->GetFactory(); + JSHandle lengthKeyHandle(thread->GlobalConstants()->GetHandledLengthString()); + JSArray *arr = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, obj, lengthKeyHandle).GetValue()->GetInt(), 0); + + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(100)), true, true, true); + + EcmaString *string1 = *factory->NewFromString("1"); + JSHandle key1(thread, static_cast(string1)); + JSHandle index1(thread, JSTaggedValue(1)); + EXPECT_TRUE(JSArray::DefineOwnProperty(thread, JSHandle(obj), key1, desc)); + EXPECT_EQ(JSArray::GetProperty(thread, obj, lengthKeyHandle).GetValue()->GetInt(), 2); + JSTaggedValue v = JSArray::GetProperty(thread, obj, key1).GetValue().GetTaggedValue(); + EXPECT_EQ(v.GetInt(), 100); + v = JSArray::GetProperty(thread, obj, index1).GetValue().GetTaggedValue(); + EXPECT_EQ(v.GetInt(), 100); + EXPECT_EQ(JSArray::GetProperty(thread, obj, lengthKeyHandle).GetValue()->GetInt(), 2); + + EcmaString *string100 = *factory->NewFromString("100"); + JSHandle key100(thread, static_cast(string100)); + JSHandle index100(thread, JSTaggedValue(100)); + + EXPECT_TRUE(JSArray::DefineOwnProperty(thread, JSHandle(obj), key100, desc)); + EXPECT_EQ(JSArray::GetProperty(thread, obj, key100).GetValue()->GetInt(), 100); + EXPECT_EQ(JSArray::GetProperty(thread, obj, index100).GetValue()->GetInt(), 100); + EXPECT_EQ(JSArray::GetProperty(thread, obj, lengthKeyHandle).GetValue()->GetInt(), 101); + + EcmaString *stringx = *factory->NewFromString("2147483646"); + JSHandle keyx(thread, static_cast(stringx)); + JSHandle indexx(thread, JSTaggedValue(2147483646U)); // 2147483646U + + EXPECT_TRUE(JSArray::DefineOwnProperty(thread, JSHandle(obj), keyx, desc)); + EXPECT_EQ(JSArray::GetProperty(thread, obj, keyx).GetValue()->GetInt(), 100); + EXPECT_EQ(JSArray::GetProperty(thread, obj, indexx).GetValue()->GetInt(), 100); + EXPECT_EQ(JSArray::GetProperty(thread, obj, lengthKeyHandle).GetValue()->GetInt(), 2147483647); + + EXPECT_TRUE(JSArray::DeleteProperty(thread, JSHandle(obj), indexx)); + EXPECT_TRUE(JSArray::GetProperty(thread, obj, keyx).GetValue()->IsUndefined()); + EXPECT_TRUE(JSArray::GetProperty(thread, obj, indexx).GetValue()->IsUndefined()); +} + +HWTEST_F_L0(JSArrayTest, Next) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle values(factory->NewTaggedArray(5)); + for (int i = 0; i < 5; i++) { + values->Set(thread, i, JSTaggedValue(i)); + } + JSHandle array(JSArray::CreateArrayFromList(thread, values)); + JSHandle iter(factory->NewJSArrayIterator(array, IterationKind::KEY)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + for (int i = 0; i < 5; i++) { + ecmaRuntimeCallInfo->SetThis(iter.GetTaggedValue()); + JSTaggedValue ret = JSArrayIterator::Next(ecmaRuntimeCallInfo.get()); + JSHandle result(thread, ret); + EXPECT_EQ(JSIterator::IteratorValue(thread, result)->GetInt(), i); + } + TestHelper::TearDownFrame(thread, prev); +} + +HWTEST_F_L0(JSArrayTest, Iterator) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle values(factory->NewTaggedArray(5)); + for (int i = 0; i < 5; i++) { + values->Set(thread, i, JSTaggedValue(i)); + } + JSHandle array(JSArray::CreateArrayFromList(thread, values)); + JSHandle key_iter(factory->NewJSArrayIterator(array, IterationKind::KEY)); + JSHandle value_iter(factory->NewJSArrayIterator(array, IterationKind::VALUE)); + JSHandle iter(factory->NewJSArrayIterator(array, IterationKind::KEY_AND_VALUE)); + + for (int i = 0; i < 5; i++) { + if (i == 2) { + JSHandle key(thread, JSTaggedValue(i)); + JSObject::DeleteProperty(thread, JSHandle(array), key); + } + JSHandle key_result(JSIterator::IteratorStep(thread, key_iter)); + JSHandle value_result(JSIterator::IteratorStep(thread, value_iter)); + JSHandle iter_result(JSIterator::IteratorStep(thread, iter)); + JSHandle iter_value(JSIterator::IteratorValue(thread, iter_result)); + JSHandle index_key(thread, JSTaggedValue(0)); + JSHandle element_key(thread, JSTaggedValue(1)); + if (i == 2) { + EXPECT_EQ(i, JSIterator::IteratorValue(thread, key_result)->GetInt()); + EXPECT_EQ(JSTaggedValue::Undefined(), JSIterator::IteratorValue(thread, value_result).GetTaggedValue()); + EXPECT_EQ(i, JSObject::GetProperty(thread, iter_value, index_key).GetValue()->GetInt()); + EXPECT_EQ(JSTaggedValue::Undefined(), + JSObject::GetProperty(thread, iter_value, element_key).GetValue().GetTaggedValue()); + continue; + } + EXPECT_EQ(i, JSIterator::IteratorValue(thread, key_result)->GetInt()); + EXPECT_EQ(i, JSIterator::IteratorValue(thread, value_result)->GetInt()); + EXPECT_EQ(i, JSObject::GetProperty(thread, iter_value, index_key).GetValue()->GetInt()); + EXPECT_EQ(i, JSObject::GetProperty(thread, iter_value, element_key).GetValue()->GetInt()); + } +} +} // namespace panda::test diff --git a/ecmascript/tests/js_date_test.cpp b/ecmascript/tests/js_date_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ac23b71ceca3b26bab788e1a35b55124dbd4112 --- /dev/null +++ b/ecmascript/tests/js_date_test.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_date.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JSDateTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSDate *JSDateCreate(JSThread *thread) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + EcmaVM *ecmaVM = thread->GetEcmaVM(); + auto globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle dateFunction = globalEnv->GetDateFunction(); + JSHandle dateObject = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(dateFunction), dateFunction)); + return *dateObject; +} + +HWTEST_F_L0(JSDateTest, Create) +{ + double tm = 0.0; + JSHandle jsDate(thread, JSDateCreate(thread)); + EXPECT_EQ(jsDate->GetTimeValue(), JSTaggedValue(tm)); + EXPECT_EQ(jsDate->GetLocalOffset(), JSTaggedValue(JSDate::MAX_DOUBLE)); + tm = 28 * 60 * 60 * 1000; + jsDate->SetTimeValue(thread, JSTaggedValue(tm)); + + [[maybe_unused]] double temp = jsDate->GetTimeValue().GetDouble(); + EXPECT_EQ(jsDate->GetTimeValue(), JSTaggedValue(tm)); +} + +HWTEST_F_L0(JSDateTest, MakeTime) +{ + double const day1 = ecmascript::JSDate::MakeDay(0, 11, 31); + double const time1 = ecmascript::JSDate::MakeTime(0, 0, 0, 0); + double ms1 = ecmascript::JSDate::TimeClip(ecmascript::JSDate::MakeDate(day1, time1)); + EXPECT_EQ(ms1, -62135683200000.0); + + double const day = ecmascript::JSDate::MakeDay(-1, 11, 31); + double const time = ecmascript::JSDate::MakeTime(0, 0, 0, 0); + double ms = ecmascript::JSDate::TimeClip(ecmascript::JSDate::MakeDate(day, time)); + EXPECT_EQ(ms, -62167305600000.0); +} + +HWTEST_F_L0(JSDateTest, IsoParseStringToMs) +{ + CString str = "2020-11-19T12:18:18.132Z"; + JSTaggedValue ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605788298132.0); + + str = "2020-11-19Z"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605744000000.0); + + str = "2020-11"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1604188800000.0); + + str = "+275760-09-13T00:00:00.000Z"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 8640000000000000.0); + + str = "-271821-04-20T00:00:00.000Z"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), -8640000000000000.0); + + str = "2020T12:18Z"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1577881080000.0); + + str = "2020T12:18:17.231Z"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1577881097231.0); + + str = "2020-11T12:18:17.231Z"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1604233097231.0); + + str = "1645-11T12:18:17.231+08:00"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), -10229658102769.0); + + str = "2020-11-19T12:18-08:12"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605817800000.0); +} + +HWTEST_F_L0(JSDateTest, LocalParseStringToMs) +{ + CString str = "Thu Nov 19 2020 20:18:18 GMT+0800"; + JSTaggedValue ms = ecmascript::JSDate::LocalParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605788298000.0); + + str = "Thu Nov 19 2020 20:18 GMT-0800"; + ms = ecmascript::JSDate::LocalParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605845880000.0); + + str = "Thu Nov 03 2093 04:18 GMT"; + ms = ecmascript::JSDate::LocalParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 3908060280000.0); + + str = "Thu Nov 19 1820 GMT+1232"; + ms = ecmascript::JSDate::LocalParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), -4705734720000.0); +} + +HWTEST_F_L0(JSDateTest, UtcParseStringToMs) +{ + CString str = "Thu, 19 Nov 2020 20:18:18 GMT+0800"; + JSTaggedValue ms = ecmascript::JSDate::UtcParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605788298000.0); + + str = "Thu, 19 Nov 2020 20:18 GMT-0800"; + ms = ecmascript::JSDate::UtcParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605845880000.0); + + str = "Thu 03 Jun 2093 04:18 GMT"; + ms = ecmascript::JSDate::UtcParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 3894841080000.0); + + str = "Thu 19 Nov 1820 GMT+1232"; + ms = ecmascript::JSDate::UtcParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), -4705734720000.0); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_forin_iterator_test.cpp b/ecmascript/tests/js_forin_iterator_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fd3f3e615494062cb1d938b8dcc6a2593d358f97 --- /dev/null +++ b/ecmascript/tests/js_forin_iterator_test.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tests/test_helper.h" + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_for_in_iterator.h" +#include "ecmascript/js_handle.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JSForinIteratorTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(JSForinIteratorTest, Create) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle grandfather = JSObject::ObjectCreate(thread, nullHandle); + EXPECT_TRUE(grandfather->GetPrototype(thread).IsNull()); + + JSHandle father = JSObject::ObjectCreate(thread, grandfather); + + JSHandle son = JSObject::ObjectCreate(thread, father); + + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString("key1")); + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromString("key2")); + JSHandle key3(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle key1Value(thread, JSTaggedValue(1)); + JSHandle key2Value(thread, JSTaggedValue(2)); + JSHandle key3Value(thread, JSTaggedValue(3)); + + JSObject::SetProperty(thread, JSHandle(grandfather), key3, key3Value); + JSObject::SetProperty(thread, JSHandle(father), key2, key2Value); + + JSObject::SetProperty(thread, JSHandle(son), key1, key1Value); + JSObject::SetProperty(thread, JSHandle(son), key2, key1Value); + JSObject::SetProperty(thread, JSHandle(son), key3, key1Value); + + JSHandle it = thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(JSHandle(son)); + std::pair n1 = JSForInIterator::NextInternal(thread, it); + EXPECT_EQ(n1.first, key1.GetTaggedValue()); + EXPECT_FALSE(n1.second); + + std::pair n2 = JSForInIterator::NextInternal(thread, it); + EXPECT_EQ(n2.first, key2.GetTaggedValue()); + EXPECT_FALSE(n2.second); + + std::pair n3 = JSForInIterator::NextInternal(thread, it); + EXPECT_EQ(n3.first, key3.GetTaggedValue()); + EXPECT_FALSE(n3.second); + + std::pair n4 = JSForInIterator::NextInternal(thread, it); + EXPECT_EQ(n4.first, JSTaggedValue::Undefined()); + EXPECT_TRUE(n4.second); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_function_test.cpp b/ecmascript/tests/js_function_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9fdd1752294d9f93096cdfe599d061c4f236b92a --- /dev/null +++ b/ecmascript/tests/js_function_test.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_function.h" +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function_extra_info.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +using namespace panda::ecmascript::base; + +namespace panda::test { +class JSFunctionTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSFunction *JSObjectCreate(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + return globalEnv->GetObjectFunction().GetObject(); +} + +HWTEST_F_L0(JSFunctionTest, Create) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle funHandle = thread->GetEcmaVM()->GetFactory()->NewJSFunction(env); + EXPECT_TRUE(*funHandle != nullptr); + EXPECT_EQ(funHandle->GetProtoOrDynClass(), JSTaggedValue::Hole()); + + JSHandle lexicalEnv = thread->GetEcmaVM()->GetFactory()->NewLexicalEnv(0); + funHandle->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue()); + EXPECT_EQ(funHandle->GetLexicalEnv(), lexicalEnv.GetTaggedValue()); + EXPECT_TRUE(*lexicalEnv != nullptr); +} +HWTEST_F_L0(JSFunctionTest, MakeConstructor) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle func = thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, static_cast(nullptr), + FunctionKind::BASE_CONSTRUCTOR); + EXPECT_TRUE(*func != nullptr); + JSHandle funcHandle(func); + func->GetJSHClass()->SetExtensible(true); + + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle obj = JSObject::ObjectCreate(thread, nullHandle); + JSHandle objValue(obj); + + JSFunction::MakeConstructor(thread, func, objValue); + + JSHandle constructorKey(thread->GetEcmaVM()->GetFactory()->NewFromString("constructor")); + + JSHandle protoKey(thread->GetEcmaVM()->GetFactory()->NewFromString("prototype")); + JSTaggedValue proto = JSObject::GetProperty(thread, funcHandle, protoKey).GetValue().GetTaggedValue(); + JSTaggedValue constructor = + JSObject::GetProperty(thread, JSHandle(obj), constructorKey).GetValue().GetTaggedValue(); + EXPECT_EQ(constructor, funcHandle.GetTaggedValue()); + EXPECT_EQ(proto, obj.GetTaggedValue()); + EXPECT_EQ(func->GetFunctionKind(), FunctionKind::BASE_CONSTRUCTOR); +} + +HWTEST_F_L0(JSFunctionTest, OrdinaryHasInstance) +{ + JSHandle objFun(thread, JSObjectCreate(thread)); + + JSHandle jsobject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle obj(thread, jsobject.GetTaggedValue()); + EXPECT_TRUE(*jsobject != nullptr); + + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle constructor = globalEnv->GetObjectFunction(); + EXPECT_TRUE(ecmascript::JSFunction::OrdinaryHasInstance(thread, constructor, obj)); +} + +JSTaggedValue TestInvokeInternal(EcmaRuntimeCallInfo *argv) +{ + if (argv->GetArgsNumber() == 1 && argv->GetCallArg(0).GetTaggedValue() == JSTaggedValue(1)) { + return BuiltinsBase::GetTaggedBoolean(true); + } else { + return BuiltinsBase::GetTaggedBoolean(false); + } +} + +HWTEST_F_L0(JSFunctionTest, Invoke) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle dynclass(thread, JSObjectCreate(thread)); + JSHandle callee( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(dynclass), dynclass)); + EXPECT_TRUE(*callee != nullptr); + + char keyArray[] = "invoked"; + JSHandle calleeKey(thread->GetEcmaVM()->GetFactory()->NewFromString(&keyArray[0])); + JSHandle calleeFunc = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestInvokeInternal)); + calleeFunc->SetCallable(true); + JSHandle calleeValue(calleeFunc); + JSObject::SetProperty(thread, JSHandle(callee), calleeKey, calleeValue); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle arguments = factory->NewTaggedArray(1); + arguments->Set(thread, 0, JSTaggedValue(1)); + JSTaggedValue res = JSFunction::Invoke(thread, callee, calleeKey, arguments); + + JSTaggedValue ruler = BuiltinsBase::GetTaggedBoolean(true); + EXPECT_EQ(res.GetRawData(), ruler.GetRawData()); +} + +HWTEST_F_L0(JSFunctionTest, SetSymbolFunctionName) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle jsFunction = factory->NewJSFunction(env); + JSHandle symbol = factory->NewPublicSymbolWithChar("name"); + JSHandle name = factory->NewFromString("[name]"); + JSHandle prefix(thread, JSTaggedValue::Undefined()); + JSFunction::SetFunctionName(thread, JSHandle(jsFunction), JSHandle(symbol), prefix); + JSHandle functionName = + JSFunctionBase::GetFunctionName(thread, JSHandle(jsFunction)); + EXPECT_TRUE(functionName->IsString()); + EXPECT_TRUE(EcmaString::StringsAreEqual(*(JSHandle(functionName)), *name)); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_handle_test.cpp b/ecmascript/tests/js_handle_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1068f610ae7c692c2d3e3c1df83e4f908826ee30 --- /dev/null +++ b/ecmascript/tests/js_handle_test.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_handle_collection.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JSHandleTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + CVector> TestGlobalHandleNewBlock(GlobalHandleCollection global); + void TestGlobalHandleFree(GlobalHandleCollection global); +}; + +CVector> JSHandleTest::TestGlobalHandleNewBlock(GlobalHandleCollection global) +{ + ecmascript::EcmaHandleScope scope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + CString test = "test"; + CVector> vec; + // 10 : test case + for (int i = 0; i < 10; i++) { + test.append(ToCString(i)); + vec.push_back(global.NewHandle(factory->NewFromString(test).GetTaggedType())); + } + + [[maybe_unused]] auto storage = thread->GetGlobalHandleStorage(); + ASSERT(storage->GetNodes()->size() == 1); + return vec; +} + +HWTEST_F_L0(JSHandleTest, GlobalHandleNewBlock) +{ + GlobalHandleCollection global(thread); + CVector> vec = TestGlobalHandleNewBlock(global); + + // trigger GC + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + factory->NewFromString("trigger gc"); + + for (auto v : vec) { + global.Dispose(v); + } +} + +void JSHandleTest::TestGlobalHandleFree(GlobalHandleCollection global) +{ + EcmaHandleScope scope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + CString test = "test"; + // 10 : test case + for (int i = 0; i < 10; i++) { + test.append(ToCString(i)); + JSHandle str = global.NewHandle(factory->NewFromString(test).GetTaggedType()); + global.Dispose(str); + } + + [[maybe_unused]] auto storage = thread->GetGlobalHandleStorage(); + ASSERT(storage->GetNodes()->size() == 1); +} + +HWTEST_F_L0(JSHandleTest, GlobalHandleFree) +{ + GlobalHandleCollection global(thread); + TestGlobalHandleFree(global); +} + +HWTEST_F_L0(JSHandleTest, HandleScopeFree) +{ + // enable this check after add zapfree in ecma handlescope + JSHandle string = thread->GetEcmaVM()->GetFactory()->NewFromString("test1"); + { + ecmascript::EcmaHandleScope scope(thread); + JSHandle string2 = thread->GetEcmaVM()->GetFactory()->NewFromString("test2"); + string = string2; + } +} + +HWTEST_F_L0(JSHandleTest, NewHandle) +{ + JSHandle string = thread->GetEcmaVM()->GetFactory()->NewFromString("test1"); + JSHandle string2(string); + JSHandle string3 = thread->GetEcmaVM()->GetFactory()->NewFromString("test2"); + string = string3; + ASSERT_TRUE(string2.GetTaggedValue().GetTaggedObject() != string.GetTaggedValue().GetTaggedObject()); + JSMutableHandle mutable_string( + thread, thread->GetEcmaVM()->GetFactory()->NewFromString("test1").GetTaggedValue()); + JSHandle mutable_string2(mutable_string); + JSHandle mutable_string3 = thread->GetEcmaVM()->GetFactory()->NewFromString("test2"); + mutable_string.Update(mutable_string3.GetTaggedValue()); + ASSERT_TRUE(mutable_string.GetTaggedValue().GetTaggedObject() == + mutable_string2.GetTaggedValue().GetTaggedObject()); +} + +HWTEST_F_L0(JSHandleTest, NewHandleScope) +{ + EcmaHandleScope scope(thread); + + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromString("test"); + for (int32_t i = 10; i > 0; i--) { + JSHandle(thread, str.GetTaggedValue()); + } + + for (int i = 5; i > 0; i--) { + int32_t loop = 2; + while (loop) { + ecmascript::EcmaHandleScope scope2(thread); + + JSHandle str2 = thread->GetEcmaVM()->GetFactory()->NewFromString("test2"); + for (int32_t i = 1024; i > 0; i--) { + JSHandle(thread, str.GetTaggedValue()); + } + str2 = thread->GetEcmaVM()->GetFactory()->NewFromString("test3"); + loop--; + } + } +} +} // namespace panda::test diff --git a/ecmascript/tests/js_iterator_test.cpp b/ecmascript/tests/js_iterator_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd22813bdebc020e8e3d4c33fc093ca60760cff8 --- /dev/null +++ b/ecmascript/tests/js_iterator_test.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_array_iterator.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +namespace panda::test { +class JSIteratorTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(JSIteratorTest, GetIterator) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle data(factory->NewTaggedArray(2)); + data->Set(thread, 0, JSTaggedValue(1)); + data->Set(thread, 1, JSTaggedValue(1)); + JSHandle array(JSArray::CreateArrayFromList(thread, data)); + EXPECT_TRUE(array->IsArray(thread)); + JSHandle iter(JSIterator::GetIterator(thread, array)); + EXPECT_TRUE(iter->IsJSArrayIterator()); + EXPECT_TRUE(iter->GetIteratedArray().IsArray(thread)); +} + +HWTEST_F_L0(JSIteratorTest, IteratorNext) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle valueStr = thread->GlobalConstants()->GetHandledValueString(); + + JSHandle data(factory->NewTaggedArray(1)); + data->Set(thread, 0, JSTaggedValue(1)); + JSHandle array(JSArray::CreateArrayFromList(thread, data)); + JSHandle iter(JSIterator::GetIterator(thread, array)); + JSHandle result(JSIterator::IteratorNext(thread, iter)); + JSHandle resultValue(JSObject::GetProperty(thread, result, valueStr).GetValue()); + EXPECT_EQ(resultValue->GetInt(), 1); +} + +HWTEST_F_L0(JSIteratorTest, IteratorComplete) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle data(factory->NewTaggedArray(2)); + data->Set(thread, 0, JSTaggedValue(1)); + data->Set(thread, 1, JSTaggedValue(1)); + JSHandle array(JSArray::CreateArrayFromList(thread, data)); + JSHandle iter(JSIterator::GetIterator(thread, array)); + JSHandle result1(JSIterator::IteratorNext(thread, iter)); + EXPECT_EQ(false, JSIterator::IteratorComplete(thread, result1)); + JSHandle result2(JSIterator::IteratorNext(thread, iter)); + EXPECT_EQ(false, JSIterator::IteratorComplete(thread, result2)); + JSHandle result3(JSIterator::IteratorNext(thread, iter)); + EXPECT_EQ(true, JSIterator::IteratorComplete(thread, result3)); +} + +HWTEST_F_L0(JSIteratorTest, IteratorValue) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle data(factory->NewTaggedArray(3)); + data->Set(thread, 0, JSTaggedValue(1)); + data->Set(thread, 1, JSTaggedValue(1)); + data->Set(thread, 2, JSTaggedValue(1)); + JSHandle array(JSArray::CreateArrayFromList(thread, data)); + JSHandle iter(JSIterator::GetIterator(thread, array)); + JSHandle result(JSIterator::IteratorNext(thread, iter)); + JSHandle resultValue(JSIterator::IteratorValue(thread, result)); + EXPECT_EQ(resultValue->GetInt(), 1); +} + +HWTEST_F_L0(JSIteratorTest, IteratorStep) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle data(factory->NewTaggedArray(2)); + data->Set(thread, 0, JSTaggedValue(1)); + data->Set(thread, 1, JSTaggedValue(2)); + JSHandle array(JSArray::CreateArrayFromList(thread, data)); + JSHandle iter(JSIterator::GetIterator(thread, array)); + JSHandle result1(JSIterator::IteratorStep(thread, iter)); + EXPECT_EQ(JSIterator::IteratorValue(thread, result1)->GetInt(), 1); + JSHandle result2(JSIterator::IteratorStep(thread, iter)); + EXPECT_EQ(JSIterator::IteratorValue(thread, result2)->GetInt(), 2); + JSHandle result3(JSIterator::IteratorStep(thread, iter)); + EXPECT_EQ(result3.GetTaggedValue(), JSTaggedValue::False()); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_map_test.cpp b/ecmascript/tests/js_map_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..42d8727996ebca4f0f9e8e0d48ec6c310f8b5e65 --- /dev/null +++ b/ecmascript/tests/js_map_test.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/js_map.h" +#include "ecmascript/js_map_iterator.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/linked_hash_table-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_hash_table-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +namespace panda::test { +class JSMapTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + +protected: + JSMap *CreateMap() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle constructor = env->GetBuiltinsMapFunction(); + JSHandle map = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + LinkedHashMap *hashMap = LinkedHashMap::Cast(LinkedHashMap::Create(thread).GetTaggedObject()); + map->SetLinkedMap(thread, JSTaggedValue(hashMap)); + return *map; + } +}; + +HWTEST_F_L0(JSMapTest, MapCreate) +{ + JSMap *map = CreateMap(); + EXPECT_TRUE(map != nullptr); +} + +HWTEST_F_L0(JSMapTest, AddAndHas) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // create jsMap + JSHandle map(thread, CreateMap()); + + JSHandle key(factory->NewFromString("key")); + JSHandle value(thread, JSTaggedValue(1)); + JSMap::Set(thread, map, key, value); + EXPECT_TRUE(map->Has(key.GetTaggedValue())); +} + +HWTEST_F_L0(JSMapTest, DeleteAndGet) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // create jsMap + JSHandle map(thread, CreateMap()); + + // add 40 keys + char keyArray[] = "key0"; + for (int i = 0; i < 40; i++) { + keyArray[3] = '1' + i; + JSHandle key(factory->NewFromString(keyArray)); + JSHandle value(thread, JSTaggedValue(i)); + JSMap::Set(thread, map, key, value); + EXPECT_TRUE(map->Has(key.GetTaggedValue())); + } + EXPECT_EQ(map->GetSize(), 40); + // whether jsMap has delete key + keyArray[3] = '1' + 8; + JSHandle deleteKey(factory->NewFromString(keyArray)); + EXPECT_EQ(map->GetValue(8), JSTaggedValue(8)); + JSMap::Delete(thread, map, deleteKey); + EXPECT_FALSE(map->Has(deleteKey.GetTaggedValue())); + EXPECT_EQ(map->GetSize(), 39); +} + +HWTEST_F_L0(JSMapTest, Iterator) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle map(thread, CreateMap()); + for (int i = 0; i < 5; i++) { + JSHandle key(thread, JSTaggedValue(i)); + JSHandle value(thread, JSTaggedValue(i + 10)); + JSMap::Set(thread, map, key, value); + } + + JSHandle keyIter(factory->NewJSMapIterator(map, IterationKind::KEY)); + JSHandle valueIter(factory->NewJSMapIterator(map, IterationKind::VALUE)); + JSHandle iter(factory->NewJSMapIterator(map, IterationKind::KEY_AND_VALUE)); + + JSHandle indexKey(thread, JSTaggedValue(0)); + JSHandle elementKey(thread, JSTaggedValue(1)); + + JSHandle keyResult0 = JSIterator::IteratorStep(thread, keyIter); + JSHandle valueResult0 = JSIterator::IteratorStep(thread, valueIter); + JSHandle result0 = JSIterator::IteratorStep(thread, iter); + + EXPECT_EQ(0, JSIterator::IteratorValue(thread, keyResult0)->GetInt()); + EXPECT_EQ(10, JSIterator::IteratorValue(thread, valueResult0)->GetInt()); + JSHandle result0Handle = JSIterator::IteratorValue(thread, result0); + EXPECT_EQ(0, JSObject::GetProperty(thread, result0Handle, indexKey).GetValue()->GetInt()); + EXPECT_EQ(10, JSObject::GetProperty(thread, result0Handle, elementKey).GetValue()->GetInt()); + + JSHandle keyResult1 = JSIterator::IteratorStep(thread, keyIter); + EXPECT_EQ(1, JSIterator::IteratorValue(thread, keyResult1)->GetInt()); + for (int i = 0; i < 3; i++) { + JSHandle key(thread, JSTaggedValue(i)); + JSMap::Delete(thread, map, key); + } + JSHandle keyResult2 = JSIterator::IteratorStep(thread, keyIter); + EXPECT_EQ(3, JSIterator::IteratorValue(thread, keyResult2)->GetInt()); + JSHandle keyResult3 = JSIterator::IteratorStep(thread, keyIter); + EXPECT_EQ(4, JSIterator::IteratorValue(thread, keyResult3)->GetInt()); + JSHandle key(thread, JSTaggedValue(5)); + JSMap::Set(thread, map, key, key); + JSHandle keyResult4 = JSIterator::IteratorStep(thread, keyIter); + + EXPECT_EQ(5, JSIterator::IteratorValue(thread, keyResult4)->GetInt()); + JSHandle keyResult5 = JSIterator::IteratorStep(thread, keyIter); + EXPECT_EQ(JSTaggedValue::False(), keyResult5.GetTaggedValue()); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_object_test.cpp b/ecmascript/tests/js_object_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..313aabf72342136ba7a9d6901bbd8b1e7dba2f15 --- /dev/null +++ b/ecmascript/tests/js_object_test.cpp @@ -0,0 +1,1296 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/base/builtins_base.h" + +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/ic/proto_change_details.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/object_operator.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tagged_dictionary.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/weak_vector-inl.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; + +namespace panda::test { +class JSObjectTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +static JSFunction *JSObjectTestCreate(JSThread *thread) +{ + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + return globalEnv->GetObjectFunction().GetObject(); +} + +HWTEST_F_L0(JSObjectTest, Create) +{ + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle jsobject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_TRUE(*jsobject != nullptr); +} + +HWTEST_F_L0(JSObjectTest, SetProperty) +{ + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle jsobject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_TRUE(*jsobject != nullptr); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + JSObject::SetProperty(thread, JSHandle(jsobject), key, value); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(jsobject), key).GetValue()->GetInt(), 1); + + JSHandle value2(thread, JSTaggedValue(2)); + JSObject::SetProperty(thread, JSHandle(jsobject), key, value2); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(jsobject), key).GetValue()->GetInt(), 2); +} + +HWTEST_F_L0(JSObjectTest, GetProperty) +{ + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_TRUE(*obj != nullptr); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + EXPECT_TRUE(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->IsUndefined()); + + JSObject::SetProperty(thread, JSHandle(obj), key, value); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); +} + +HWTEST_F_L0(JSObjectTest, DeleteProperty) +{ + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_TRUE(*obj != nullptr); + + char array[] = "print"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + JSObject::DeleteProperty(thread, (obj), key); + EXPECT_TRUE(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->IsUndefined()); + + JSObject::SetProperty(thread, JSHandle(obj), key, value); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); + + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromString("print_test")); + JSObject::SetProperty(thread, JSHandle(obj), key2, + JSHandle(thread, JSTaggedValue(10))); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key2).GetValue()->GetInt(), 10); + + JSObject::DeleteProperty(thread, (obj), key); + EXPECT_TRUE(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->IsUndefined()); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key2).GetValue()->GetInt(), 10); +} + +HWTEST_F_L0(JSObjectTest, DeletePropertyGlobal) +{ + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle global(thread, globalEnv->GetGlobalObject()); + JSHandle printKey(thread->GetEcmaVM()->GetFactory()->NewFromString("print")); + JSHandle printTestKey(thread->GetEcmaVM()->GetFactory()->NewFromString("print_test")); + + JSHandle value = JSObject::GetProperty(thread, global, printKey).GetValue(); + + JSObject::SetProperty(thread, global, printTestKey, value); + + JSTaggedValue val2 = JSObject::GetProperty(thread, global, printTestKey).GetValue().GetTaggedValue(); + EXPECT_EQ(val2, value.GetTaggedValue()); + JSTaggedValue::DeletePropertyOrThrow(thread, global, printTestKey); + JSTaggedValue val3 = JSObject::GetProperty(thread, global, printKey).GetValue().GetTaggedValue(); + EXPECT_NE(val3, JSTaggedValue::Undefined()); +} + +HWTEST_F_L0(JSObjectTest, GetPropertyInPrototypeChain) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle grandfather = JSObject::ObjectCreate(thread, nullHandle); + JSHandle father = JSObject::ObjectCreate(thread, grandfather); + JSHandle son = JSObject::ObjectCreate(thread, father); + + JSHandle sonKey(thread->GetEcmaVM()->GetFactory()->NewFromString("key1")); + JSHandle fatherKey(thread->GetEcmaVM()->GetFactory()->NewFromString("key2")); + JSHandle grandfatherKey(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle sonValue(thread, JSTaggedValue(1)); + JSHandle fatherValue(thread, JSTaggedValue(2)); + JSHandle grandfatherValue(thread, JSTaggedValue(3)); + + JSObject::SetProperty(thread, JSHandle(son), sonKey, sonValue); + JSObject::SetProperty(thread, JSHandle(father), fatherKey, fatherValue); + JSObject::SetProperty(thread, JSHandle(grandfather), grandfatherKey, grandfatherValue); + + EXPECT_EQ(sonValue.GetTaggedValue(), + JSObject::GetProperty(thread, JSHandle(son), sonKey).GetValue().GetTaggedValue()); + EXPECT_EQ(fatherValue.GetTaggedValue(), + JSObject::GetProperty(thread, JSHandle(son), fatherKey).GetValue().GetTaggedValue()); + EXPECT_EQ(grandfatherValue.GetTaggedValue(), + JSObject::GetProperty(thread, JSHandle(son), grandfatherKey).GetValue().GetTaggedValue()); +} + +HWTEST_F_L0(JSObjectTest, PropertyAttribute) +{ + JSHandle constructor(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor); + JSHandle obj2 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor); + + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + + JSHandle value1(thread, JSTaggedValue(1)); + JSHandle value2(thread, JSTaggedValue(2)); + + // test set property + PropertyDescriptor desc(thread); + desc.SetValue(value1); + desc.SetWritable(false); + JSObject::DefineOwnProperty(thread, obj1, key1, desc); + JSObject::SetProperty(thread, JSHandle(obj1), key1, value2); + JSObject::SetProperty(thread, JSHandle(obj2), key1, value1); + JSObject::SetProperty(thread, JSHandle(obj2), key1, value2); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), + value1.GetTaggedValue()); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj2), key1).GetValue().GetTaggedValue(), + value2.GetTaggedValue()); + + // test delete property + PropertyDescriptor desc1(thread); + desc1.SetValue(value1); + desc1.SetConfigurable(false); + JSObject::DefineOwnProperty(thread, obj1, key2, desc1); + JSObject::SetProperty(thread, JSHandle(obj1), key2, value1); + JSObject::SetProperty(thread, JSHandle(obj2), key2, value1); + JSObject::DeleteProperty(thread, (obj1), key2); + JSObject::DeleteProperty(thread, (obj2), key2); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key2).GetValue().GetTaggedValue(), + value1.GetTaggedValue()); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj2), key2).GetValue().GetTaggedValue(), + JSTaggedValue::Undefined()); +} + +HWTEST_F_L0(JSObjectTest, CreateDataProperty) +{ + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_TRUE(*obj != nullptr); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + bool success = JSObject::CreateDataProperty(thread, obj, key, value); + EXPECT_TRUE(success); + + success = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(obj), key); + EXPECT_TRUE(success); + + PropertyDescriptor desc(thread); + success = JSObject::GetOwnProperty(thread, obj, key, desc); + EXPECT_TRUE(success); + EXPECT_EQ(true, desc.IsWritable()); + EXPECT_EQ(true, desc.IsEnumerable()); + EXPECT_EQ(true, desc.IsConfigurable()); +} + +HWTEST_F_L0(JSObjectTest, CreateMethodProperty) +{ + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_TRUE(*obj != nullptr); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + bool success = JSObject::CreateMethodProperty(thread, obj, key, value); + EXPECT_TRUE(success); + + success = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(obj), key); + EXPECT_TRUE(success); + + PropertyDescriptor desc(thread); + success = JSObject::GetOwnProperty(thread, obj, key, desc); + EXPECT_TRUE(success); + EXPECT_EQ(true, desc.IsWritable()); + EXPECT_EQ(false, desc.IsEnumerable()); + EXPECT_EQ(true, desc.IsConfigurable()); +} + +HWTEST_F_L0(JSObjectTest, DefinePropertyOrThrow) +{ + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_TRUE(*obj != nullptr); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + bool success = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc1); + EXPECT_TRUE(success); + PropertyDescriptor descRes1(thread); + success = JSObject::GetOwnProperty(thread, obj, key, descRes1); + EXPECT_TRUE(success); + EXPECT_EQ(1, descRes1.GetValue()->GetInt()); + EXPECT_EQ(true, descRes1.IsWritable()); + EXPECT_EQ(true, descRes1.IsEnumerable()); + EXPECT_EQ(true, descRes1.IsConfigurable()); + + PropertyDescriptor desc2(thread, false, true, true); + success = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc2); + EXPECT_TRUE(success); + PropertyDescriptor descRes2(thread); + success = JSObject::GetOwnProperty(thread, obj, key, descRes2); + EXPECT_TRUE(success); + EXPECT_EQ(1, descRes2.GetValue()->GetInt()); + EXPECT_EQ(false, descRes2.IsWritable()); + EXPECT_EQ(true, descRes2.IsEnumerable()); + EXPECT_EQ(true, descRes2.IsConfigurable()); + + PropertyDescriptor desc3(thread); + desc3.SetWritable(false); + desc3.SetEnumerable(false); + desc3.SetConfigurable(false); + success = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc3); + EXPECT_TRUE(success); + PropertyDescriptor descRes3(thread); + success = JSObject::GetOwnProperty(thread, obj, key, descRes3); + EXPECT_TRUE(success); + EXPECT_EQ(1, descRes3.GetValue()->GetInt()); + EXPECT_EQ(false, descRes3.IsWritable()); + EXPECT_EQ(false, descRes3.IsEnumerable()); + EXPECT_EQ(false, descRes3.IsConfigurable()); + + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(2))); + success = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc4); + EXPECT_FALSE(success); +} + +HWTEST_F_L0(JSObjectTest, HasProperty) +{ + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_TRUE(*obj != nullptr); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + bool flag = JSObject::HasProperty(thread, obj, key); + EXPECT_FALSE(flag); + + JSObject::SetProperty(thread, JSHandle(obj), key, value); + flag = JSObject::HasProperty(thread, obj, key); + EXPECT_TRUE(flag); + + JSObject::DeleteProperty(thread, (obj), key); + flag = JSObject::HasProperty(thread, obj, key); + EXPECT_FALSE(flag); +} + +HWTEST_F_L0(JSObjectTest, HasPropertyWithProtoType) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle grandfather = JSObject::ObjectCreate(thread, nullHandle); + JSHandle father = JSObject::ObjectCreate(thread, grandfather); + JSHandle son = JSObject::ObjectCreate(thread, father); + + auto testGrand = grandfather->GetPrototype(thread); + auto testFather = father->GetPrototype(thread); + auto testSon = son->GetPrototype(thread); + EXPECT_TRUE(testSon != testFather); + EXPECT_TRUE(testGrand != testFather); + JSHandle sonKey(thread->GetEcmaVM()->GetFactory()->NewFromString("key1")); + JSHandle fatherKey(thread->GetEcmaVM()->GetFactory()->NewFromString("key2")); + JSHandle grandfatherKey(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle sonValue(thread, JSTaggedValue(1)); + JSHandle fatherValue(thread, JSTaggedValue(2)); + JSHandle grandfatherValue(thread, JSTaggedValue(3)); + + JSObject::SetProperty(thread, JSHandle(son), sonKey, sonValue); + JSObject::SetProperty(thread, JSHandle(father), fatherKey, fatherValue); + JSObject::SetProperty(thread, JSHandle(grandfather), grandfatherKey, grandfatherValue); + + bool flag = JSObject::HasProperty(thread, son, sonKey); + EXPECT_TRUE(flag); + flag = JSObject::HasProperty(thread, son, fatherKey); + EXPECT_TRUE(flag); + flag = JSObject::HasProperty(thread, son, grandfatherKey); + EXPECT_TRUE(flag); +} + +HWTEST_F_L0(JSObjectTest, HasOwnProperty) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle grandfather = JSObject::ObjectCreate(thread, nullHandle); + JSHandle father = JSObject::ObjectCreate(thread, grandfather); + JSHandle son = JSObject::ObjectCreate(thread, father); + + JSHandle sonKey(thread->GetEcmaVM()->GetFactory()->NewFromString("key1")); + JSHandle fatherKey(thread->GetEcmaVM()->GetFactory()->NewFromString("key2")); + JSHandle grandfatherKey(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle sonValue(thread, JSTaggedValue(1)); + JSHandle fatherValue(thread, JSTaggedValue(2)); + JSHandle grandfatherValue(thread, JSTaggedValue(3)); + + JSObject::SetProperty(thread, JSHandle(son), sonKey, sonValue); + JSObject::SetProperty(thread, JSHandle(father), fatherKey, fatherValue); + JSObject::SetProperty(thread, JSHandle(grandfather), grandfatherKey, grandfatherValue); + + bool flag = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(son), sonKey); + EXPECT_TRUE(flag); + flag = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(son), fatherKey); + EXPECT_FALSE(flag); + flag = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(son), grandfatherKey); + EXPECT_FALSE(flag); +} + +HWTEST_F_L0(JSObjectTest, GetOwnPropertyKeys) +{ + JSHandle constructor(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor); + + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString("x")); + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromString("y")); + JSHandle key3(thread->GetEcmaVM()->GetFactory()->NewFromString("3")); + JSHandle key4(thread->GetEcmaVM()->GetFactory()->NewFromString("4")); + JSHandle value1(thread, JSTaggedValue(1)); + JSHandle value2(thread, JSTaggedValue(2)); + JSHandle value3(thread, JSTaggedValue(3)); + JSHandle value4(thread, JSTaggedValue(4)); + + JSObject::SetProperty(thread, JSHandle(obj), key1, value1); + JSObject::SetProperty(thread, JSHandle(obj), key2, value2); + JSObject::SetProperty(thread, JSHandle(obj), key3, value3); + JSObject::SetProperty(thread, JSHandle(obj), key4, value4); + + JSHandle array = JSObject::GetOwnPropertyKeys(thread, obj); + int length = array->GetLength(); + EXPECT_EQ(length, 4); + int sum = 0; + for (int i = 0; i < length; i++) { + JSHandle key(thread, array->Get(i)); + sum += JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(); + } + EXPECT_EQ(sum, 10); +} + +HWTEST_F_L0(JSObjectTest, ObjectCreateMethod) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle grandfather = JSObject::ObjectCreate(thread, nullHandle); + JSHandle father = JSObject::ObjectCreate(thread, grandfather); + JSHandle son = JSObject::ObjectCreate(thread, father); + + EXPECT_EQ(son->GetPrototype(thread), father.GetTaggedValue()); + EXPECT_EQ(father->GetPrototype(thread), grandfather.GetTaggedValue()); + EXPECT_EQ(grandfather->GetPrototype(thread), JSTaggedValue::Null()); +} + +HWTEST_F_L0(JSObjectTest, GetMethod) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle obj = JSObject::ObjectCreate(thread, nullHandle); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle func(thread->GetEcmaVM()->GetFactory()->NewJSFunction(env)); + JSHandle::Cast(func)->GetJSHClass()->SetCallable(true); + EXPECT_TRUE(*func != nullptr); + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("1")); + JSObject::SetProperty(thread, JSHandle(obj), key, func); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue(), + func.GetTaggedValue()); +} + +HWTEST_F_L0(JSObjectTest, EnumerableOwnNames) +{ + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_TRUE(*obj != nullptr); + + CString tagCStr = "x"; + JSHandle tagString = thread->GetEcmaVM()->GetFactory()->NewFromString(&tagCStr[0]); + JSHandle key(tagString); + + JSHandle value(thread, JSTaggedValue(1)); + + JSObject::SetProperty(thread, JSHandle(obj), key, value); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); + + JSHandle names = JSObject::EnumerableOwnNames(thread, obj); + + JSHandle keyFromNames(thread, JSTaggedValue(names->Get(0))); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), keyFromNames).GetValue()->GetInt(), 1); + + PropertyDescriptor descNoEnum(thread); + descNoEnum.SetEnumerable(false); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, descNoEnum); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); + + JSHandle namesNoEnum = JSObject::EnumerableOwnNames(thread, obj); + EXPECT_TRUE(namesNoEnum->GetLength() == 0); + + PropertyDescriptor descEnum(thread); + descEnum.SetConfigurable(false); + descEnum.SetEnumerable(true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, descEnum); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); + + JSHandle namesNoConfig = JSObject::EnumerableOwnNames(thread, obj); + + JSHandle keyNoConfig(thread, JSTaggedValue(namesNoConfig->Get(0))); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), keyNoConfig).GetValue()->GetInt(), 1); +} + +HWTEST_F_L0(JSObjectTest, SetIntegrityLevelSealed) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + EXPECT_TRUE(*obj1 != nullptr); + CString undefinedCStr = "x"; + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString(&undefinedCStr[0])); + JSHandle value1(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); + + // test SetIntegrityLevel::SEALED + JSHandle jsobject(obj1); + bool status1 = JSObject::SetIntegrityLevel(thread, jsobject, IntegrityLevel::SEALED); + EXPECT_TRUE(status1); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), + value1.GetTaggedValue()); + PropertyDescriptor desc1(thread); + bool success1 = JSObject::GetOwnProperty(thread, jsobject, key1, desc1); + EXPECT_TRUE(success1); + EXPECT_EQ(true, desc1.IsWritable()); + EXPECT_EQ(true, desc1.IsEnumerable()); + EXPECT_EQ(false, desc1.IsConfigurable()); +} + +HWTEST_F_L0(JSObjectTest, SetIntegrityLevelFrozen) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + EXPECT_TRUE(*obj1 != nullptr); + + CString undefinedCStr = "x"; + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString(&undefinedCStr[0])); + JSHandle value1(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); + + // test SetIntegrityLevel::FROZEN + bool status1 = JSObject::SetIntegrityLevel(thread, obj1, IntegrityLevel::FROZEN); + EXPECT_TRUE(status1); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), + value1.GetTaggedValue()); + PropertyDescriptor desc1(thread); + bool success1 = JSObject::GetOwnProperty(thread, obj1, key1, desc1); + EXPECT_TRUE(success1); + EXPECT_EQ(false, desc1.IsWritable()); + EXPECT_EQ(true, desc1.IsEnumerable()); + EXPECT_EQ(false, desc1.IsConfigurable()); +} + +HWTEST_F_L0(JSObjectTest, TestIntegrityLevelSealed) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + CString undefinedCStr = "level"; + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString(&undefinedCStr[0])); + JSHandle value1(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); + obj1->GetJSHClass()->SetExtensible(false); + + // test SetIntegrityLevel::SEALED + bool status1 = JSObject::SetIntegrityLevel(thread, obj1, IntegrityLevel::SEALED); + EXPECT_TRUE(status1); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), + value1.GetTaggedValue()); + + PropertyDescriptor desc1(thread); + bool success1 = JSObject::GetOwnProperty(thread, obj1, key1, desc1); + EXPECT_TRUE(success1); + EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, obj1, IntegrityLevel::SEALED)); + EXPECT_EQ(false, JSObject::TestIntegrityLevel(thread, obj1, IntegrityLevel::FROZEN)); +} + +HWTEST_F_L0(JSObjectTest, TestIntegrityLevelFrozen) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + CString undefinedCStr = "level"; + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString(&undefinedCStr[0])); + JSHandle value1(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); + obj1->GetJSHClass()->SetExtensible(false); + + // test SetIntegrityLevel::FROZEN + bool status1 = JSObject::SetIntegrityLevel(thread, obj1, IntegrityLevel::FROZEN); + EXPECT_TRUE(status1); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), + value1.GetTaggedValue()); + + PropertyDescriptor desc1(thread); + bool success1 = JSObject::GetOwnProperty(thread, obj1, key1, desc1); + EXPECT_TRUE(success1); + EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, obj1, IntegrityLevel::SEALED)); + EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, obj1, IntegrityLevel::FROZEN)); +} + +HWTEST_F_L0(JSObjectTest, TestIntegrityLevelWithoutProperty) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + JSHandle obj1( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1)); + JSHandle::Cast(obj1)->GetJSHClass()->SetExtensible(false); + CString undefinedCStr = "level"; + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString(&undefinedCStr[0])); + + // test SetIntegrityLevel::FROZEN + JSHandle jsobject(obj1); + bool status1 = JSObject::SetIntegrityLevel(thread, jsobject, IntegrityLevel::SEALED); + EXPECT_TRUE(status1); + + PropertyDescriptor desc1(thread); + bool success1 = JSObject::GetOwnProperty(thread, jsobject, key1, desc1); + EXPECT_TRUE(!success1); + EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, jsobject, IntegrityLevel::SEALED)); + EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, jsobject, IntegrityLevel::FROZEN)); +} + +JSTaggedValue TestGetter(EcmaRuntimeCallInfo *argv) +{ + auto thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj(BuiltinsBase::GetThis(argv)); + JSHandle key(factory->NewFromString("y")); + JSTaggedValue value = JSObject::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue(); + + return JSTaggedValue(value.GetInt() + 1); +} + +HWTEST_F_L0(JSObjectTest, Getter) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + JSHandle key1(factory->NewFromString("x")); + JSHandle key2(factory->NewFromString("y")); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle getter = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestGetter)); + + PropertyDescriptor desc1(thread); + desc1.SetGetter(JSHandle::Cast(getter)); + bool success1 = JSObject::DefineOwnProperty(thread, obj, key1, desc1); + EXPECT_TRUE(success1); + + PropertyDescriptor desc2(thread); + desc2.SetValue(JSHandle(thread, JSTaggedValue(1))); + success1 = JSObject::DefineOwnProperty(thread, obj, key2, desc2); + EXPECT_TRUE(success1); + + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key1).GetValue().GetTaggedValue(), + JSTaggedValue(2)); +} + +JSTaggedValue TestSetter(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj(BuiltinsBase::GetThis(argv)); + JSHandle key(factory->NewFromString("y")); + JSTaggedValue value(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue()); + JSHandle valueHandle(thread, JSTaggedValue(value.GetInt() + 1)); + JSObject::SetProperty(thread, JSHandle(obj), key, valueHandle); + + return JSTaggedValue(JSTaggedValue::True()); +} + +HWTEST_F_L0(JSObjectTest, Setter) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + JSHandle key1(factory->NewFromString("x")); + JSHandle key2(factory->NewFromString("y")); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle setter = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestSetter)); + + PropertyDescriptor desc1(thread); + desc1.SetSetter(JSHandle::Cast(setter)); + bool success1 = JSObject::DefineOwnProperty(thread, obj, key1, desc1); + EXPECT_TRUE(success1); + + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + success1 = JSObject::DefineOwnProperty(thread, obj, key2, desc2); + EXPECT_TRUE(success1); + + JSHandle valueHandle(thread, JSTaggedValue::Undefined()); + EXPECT_TRUE(JSObject::SetProperty(thread, JSHandle(obj), key1, valueHandle)); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key2).GetValue().GetTaggedValue(), + JSTaggedValue(2)); +} + +HWTEST_F_L0(JSObjectTest, SpeciesConstructor) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle constructorFunc = + factory->NewJSFunction(env, static_cast(nullptr), FunctionKind::BASE_CONSTRUCTOR); + JSHandle constructorFuncValue(constructorFunc); + constructorFunc->GetJSHClass()->SetExtensible(true); + JSFunction::NewJSFunctionPrototype(thread, factory, constructorFunc); + + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle undefinedValue(thread, JSTaggedValue::Undefined()); + JSHandle protoObj = JSObject::ObjectCreate(thread, nullHandle); + JSHandle protoObjValue(protoObj); + + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread, protoObjValue, constructorKey, constructorFuncValue); + + factory->NewJSObjectByConstructor(constructorFunc, JSHandle::Cast(constructorFunc)); + JSHandle speciesConstruct = + factory->NewJSFunction(env, static_cast(nullptr), FunctionKind::BASE_CONSTRUCTOR); + JSHandle speciesConstructValue(speciesConstruct); + constructorFunc->GetJSHClass()->SetExtensible(true); + JSFunction::MakeConstructor(thread, speciesConstruct, undefinedValue); + + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSObject::SetProperty(thread, constructorFuncValue, speciesSymbol, speciesConstructValue); + + JSTaggedValue speciesValue = + JSObject::SpeciesConstructor(thread, protoObj, constructorFuncValue).GetTaggedValue(); + EXPECT_EQ(speciesValue, speciesConstructValue.GetTaggedValue()); +} + +JSTaggedValue TestUndefinedGetter([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + // 10 : test case + return JSTaggedValue(10); +} + +JSTaggedValue TestUndefinedSetter([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + // 10 : test case + return JSTaggedValue(10); +} + +HWTEST_F_L0(JSObjectTest, GetterIsUndefined) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + JSHandle key(factory->NewFromString("property")); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle getter = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestUndefinedGetter)); + JSHandle setter = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestUndefinedSetter)); + JSHandle unGetter(thread, JSTaggedValue::Undefined()); + + PropertyDescriptor desc1(thread); + desc1.SetGetter(JSHandle::Cast(getter)); + desc1.SetSetter(JSHandle::Cast(setter)); + desc1.SetConfigurable(true); + desc1.SetEnumerable(true); + bool success1 = JSObject::DefineOwnProperty(thread, obj, key, desc1); + EXPECT_TRUE(success1); + + PropertyDescriptor desc2(thread); + desc2.SetGetter(unGetter); + bool success2 = JSObject::DefineOwnProperty(thread, obj, key, desc2); + EXPECT_TRUE(success2); + + PropertyDescriptor desc(thread); + bool success = JSObject::GetOwnProperty(thread, obj, key, desc); + EXPECT_TRUE(success); + EXPECT_TRUE(desc.GetSetter()->IsJSFunction()); + EXPECT_TRUE(desc.GetGetter()->IsUndefined()); +} + +HWTEST_F_L0(JSObjectTest, SetterIsUndefined) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + JSHandle key(factory->NewFromString("property")); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle getter = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestUndefinedGetter)); + JSHandle setter = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestUndefinedSetter)); + JSHandle unSetter(thread, JSTaggedValue::Undefined()); + + PropertyDescriptor desc1(thread); + desc1.SetGetter(JSHandle::Cast(getter)); + desc1.SetSetter(JSHandle::Cast(setter)); + desc1.SetConfigurable(true); + desc1.SetEnumerable(true); + bool success1 = JSObject::DefineOwnProperty(thread, obj, key, desc1); + EXPECT_TRUE(success1); + + PropertyDescriptor desc2(thread); + desc2.SetSetter(unSetter); + bool success2 = JSObject::DefineOwnProperty(thread, obj, key, desc2); + EXPECT_TRUE(success2); + + PropertyDescriptor desc(thread); + bool success = JSObject::GetOwnProperty(thread, obj, key, desc); + EXPECT_TRUE(success); + EXPECT_TRUE(desc.GetSetter()->IsUndefined()); + + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue(), + JSTaggedValue(10)); +} + +HWTEST_F_L0(JSObjectTest, HClass) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + JSHandle hc0(thread, obj1->GetJSHClass()); + + JSHandle key1(factory->NewFromString("x")); + JSHandle key2(factory->NewFromString("y")); + JSHandle key3(factory->NewFromString("z")); + JSHandle value(thread, JSTaggedValue(1)); + + JSObject::SetProperty(thread, JSHandle(obj1), key1, value); + JSHandle hc1(thread, obj1->GetJSHClass()); + EXPECT_NE(hc0.GetTaggedValue(), hc1.GetTaggedValue()); + EXPECT_EQ(hc0.GetTaggedValue(), hc1->GetParent()); + + JSObject::SetProperty(thread, JSHandle(obj1), key2, value); + JSHandle hc2(thread, obj1->GetJSHClass()); + EXPECT_NE(hc1.GetTaggedValue(), hc2.GetTaggedValue()); + EXPECT_EQ(hc1.GetTaggedValue(), hc2->GetParent()); + + JSHandle obj2 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_EQ(hc0.GetTaggedValue().GetTaggedObject(), obj2->GetJSHClass()); + + JSObject::SetProperty(thread, JSHandle(obj2), key1, value); + EXPECT_EQ(hc1.GetTaggedValue().GetTaggedObject(), obj2->GetJSHClass()); + + JSObject::SetProperty(thread, JSHandle(obj2), key3, value); + JSHandle hc3(thread, obj2->GetJSHClass()); + EXPECT_NE(hc1.GetTaggedValue().GetTaggedObject(), obj2->GetJSHClass()); + EXPECT_EQ(hc1.GetTaggedValue(), obj2->GetJSHClass()->GetParent()); + + JSHandle obj3 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_EQ(hc0.GetTaggedValue().GetTaggedObject(), obj3->GetJSHClass()); + JSObject::SetProperty(thread, JSHandle(obj3), key1, value); + EXPECT_EQ(hc1.GetTaggedValue().GetTaggedObject(), obj3->GetJSHClass()); + + JSObject::SetProperty(thread, JSHandle(obj3), key2, value); + EXPECT_EQ(hc2.GetTaggedValue().GetTaggedObject(), obj3->GetJSHClass()); + + JSObject::SetProperty(thread, JSHandle(obj3), key3, value); + EXPECT_NE(hc3.GetTaggedValue().GetTaggedObject(), obj3->GetJSHClass()); + EXPECT_EQ(hc2.GetTaggedValue(), obj3->GetJSHClass()->GetParent()); +} + +HWTEST_F_L0(JSObjectTest, FastToSlow) +{ + auto ecmaVM = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + + JSMutableHandle key(factory->NewFromString("x")); + JSMutableHandle number(thread, JSTaggedValue(0)); + JSMutableHandle newkey(thread, JSTaggedValue(0)); + JSHandle value(thread, JSTaggedValue(1)); + + factory->SetTriggerGc(false); + for (uint32_t i = 0; i < PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES; i++) { + number.Update(JSTaggedValue(i)); + number.Update(JSTaggedValue::ToString(thread, number).GetTaggedValue()); + EcmaString *newString = *factory->ConcatFromString(key, JSTaggedValue::ToString(thread, number)); + newkey.Update(JSTaggedValue(newString)); + JSObject::SetProperty(thread, JSHandle(obj1), newkey, value); + } + factory->SetTriggerGc(true); + + EXPECT_FALSE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); + + number.Update(JSTaggedValue(PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)); + number.Update(JSTaggedValue::ToString(thread, number).GetTaggedValue()); + EcmaString *newString = *factory->ConcatFromString(key, JSTaggedValue::ToString(thread, number)); + newkey.Update(JSTaggedValue(newString)); + JSObject::SetProperty(thread, JSHandle(obj1), newkey, value); + + EXPECT_TRUE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); + NameDictionary *dict = NameDictionary::Cast(obj1->GetProperties().GetTaggedObject()); + EXPECT_EQ(dict->EntriesCount(), PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES + 1); + EXPECT_EQ(dict->NextEnumerationIndex(thread), PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES + 1); +} + +HWTEST_F_L0(JSObjectTest, DeleteMiddle) +{ + auto ecmaVM = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + + JSMutableHandle key(factory->NewFromString("x")); + JSMutableHandle number(thread, JSTaggedValue(0)); + JSMutableHandle newkey(thread, JSTaggedValue(0)); + JSHandle value(thread, JSTaggedValue(1)); + + for (uint32_t i = 0; i < 10; i++) { + number.Update(JSTaggedValue(i)); + number.Update(JSTaggedValue::ToString(thread, number).GetTaggedValue()); + EcmaString *newString = *factory->ConcatFromString(key, JSTaggedValue::ToString(thread, number)); + newkey.Update(JSTaggedValue(newString)); + JSObject::SetProperty(thread, JSHandle(obj1), newkey, value); + } + + EXPECT_FALSE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); + + JSMutableHandle key5(factory->NewFromString("x5")); + JSObject::DeleteProperty(thread, (obj1), key5); + + EXPECT_TRUE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); + NameDictionary *dict = NameDictionary::Cast(obj1->GetProperties().GetTaggedObject()); + EXPECT_EQ(dict->EntriesCount(), 9); + EXPECT_FALSE(JSObject::HasProperty(thread, obj1, key5)); +} + +HWTEST_F_L0(JSObjectTest, ElementFastToSlow) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle key0(thread, JSTaggedValue(0)); + JSHandle key1(thread, JSTaggedValue(1)); + JSHandle key2(thread, JSTaggedValue(2)); + JSHandle key2000(thread, JSTaggedValue(2000)); + JSHandle keyStr(factory->NewFromString("str")); + + // test dictionary [0,1,2,...,2000] + JSHandle obj1 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_TRUE(!TaggedArray::Cast(obj1->GetElements().GetTaggedObject())->IsDictionaryMode()); + JSObject::SetProperty(thread, JSHandle(obj1), keyStr, key2); + JSObject::SetProperty(thread, JSHandle(obj1), key0, key0); + EXPECT_TRUE(!TaggedArray::Cast(obj1->GetElements().GetTaggedObject())->IsDictionaryMode()); + JSHandle dynClass(thread, obj1->GetJSHClass()); + JSObject::SetProperty(thread, JSHandle(obj1), key1, key1); + EXPECT_TRUE(!TaggedArray::Cast(obj1->GetElements().GetTaggedObject())->IsDictionaryMode()); + EXPECT_EQ(obj1->GetJSHClass(), *dynClass); + + JSObject::SetProperty(thread, JSHandle(obj1), key2000, key2000); + EXPECT_TRUE(TaggedArray::Cast(obj1->GetElements().GetTaggedObject())->IsDictionaryMode()); + JSTaggedValue value = + JSObject::GetProperty(thread, JSHandle(obj1), keyStr).GetValue().GetTaggedValue(); + EXPECT_EQ(value, key2.GetTaggedValue()); + // test holey [0,,2] + JSHandle obj2 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + JSObject::SetProperty(thread, JSHandle(obj2), key0, key0); + EXPECT_TRUE(!TaggedArray::Cast(obj2->GetElements().GetTaggedObject())->IsDictionaryMode()); + JSHandle dynClass2(thread, obj2->GetJSHClass()); + JSObject::SetProperty(thread, JSHandle(obj2), key2, key2); + EXPECT_TRUE(!TaggedArray::Cast(obj2->GetElements().GetTaggedObject())->IsDictionaryMode()); + EXPECT_EQ(obj2->GetJSHClass(), *dynClass2); + // test change attr + JSHandle obj3 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + JSObject::SetProperty(thread, JSHandle(obj3), key0, key0); + JSObject::SetProperty(thread, JSHandle(obj3), key1, key1); + JSObject::SetProperty(thread, JSHandle(obj3), key2, key2); + EXPECT_TRUE(!TaggedArray::Cast(obj3->GetElements().GetTaggedObject())->IsDictionaryMode()); + PropertyDescriptor desc(thread); + desc.SetValue(key1); + desc.SetWritable(false); + JSObject::DefineOwnProperty(thread, obj3, key1, desc); + EXPECT_TRUE(TaggedArray::Cast(obj3->GetElements().GetTaggedObject())->IsDictionaryMode()); + // test delete element + JSHandle obj4 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + JSObject::SetProperty(thread, JSHandle(obj4), key0, key0); + JSObject::SetProperty(thread, JSHandle(obj4), key1, key1); + JSObject::SetProperty(thread, JSHandle(obj4), key2, key2); + EXPECT_TRUE(!TaggedArray::Cast(obj4->GetElements().GetTaggedObject())->IsDictionaryMode()); + JSObject::DeleteProperty(thread, (obj4), key1); + EXPECT_TRUE(TaggedArray::Cast(obj4->GetElements().GetTaggedObject())->IsDictionaryMode()); + + JSHandle value1001(thread, JSTaggedValue(1001)); + JSHandle obj100 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + PropertyDescriptor desc1(thread); + desc1.SetValue(value1001); + desc1.SetWritable(false); + desc1.SetEnumerable(false); + desc1.SetConfigurable(false); + JSObject::SetProperty(thread, JSHandle(obj100), key0, key1); + JSObject::DefineOwnProperty(thread, obj100, key0, desc1); + JSTaggedValue result1001 = + JSObject::GetProperty(thread, JSHandle(obj100), key0).GetValue().GetTaggedValue(); + EXPECT_EQ(result1001, value1001.GetTaggedValue()); +} + +HWTEST_F_L0(JSObjectTest, EnableProtoChangeMarker) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle obj1 = JSObject::ObjectCreate(thread, nullHandle); + JSHandle obj2 = JSObject::ObjectCreate(thread, obj1); + JSHandle obj3 = JSObject::ObjectCreate(thread, obj2); + + JSHandle obj1Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key1")); + JSHandle obj2Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key2")); + JSHandle obj3Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle obj1Value(thread, JSTaggedValue(1)); + JSHandle obj2Value(thread, JSTaggedValue(2)); + JSHandle obj3Value(thread, JSTaggedValue(3)); + + JSObject::SetProperty(thread, JSHandle(obj1), obj1Key, obj1Value); + JSObject::SetProperty(thread, JSHandle(obj2), obj2Key, obj2Value); + JSObject::SetProperty(thread, JSHandle(obj3), obj3Key, obj3Value); + JSHandle obj3Dynclass(thread, obj3->GetJSHClass()); + JSHandle resultMarker = JSHClass::EnableProtoChangeMarker(thread, obj3Dynclass); + EXPECT_TRUE(resultMarker->IsProtoChangeMarker()); + bool hasChanged = ProtoChangeMarker::Cast(resultMarker->GetTaggedObject())->GetHasChanged(); + EXPECT_TRUE(!hasChanged); + + JSHandle obj1Dynclass(thread, obj1->GetJSHClass()); + JSHandle obj2Dynclass(thread, obj2->GetJSHClass()); + JSTaggedValue obj2Marker = obj2Dynclass->GetProtoChangeMarker(); + EXPECT_TRUE(obj2Marker.IsProtoChangeMarker()); + bool hasChanged2 = ProtoChangeMarker::Cast(obj2Marker.GetTaggedObject())->GetHasChanged(); + EXPECT_TRUE(!hasChanged2); + + JSTaggedValue obj1Marker = obj1Dynclass->GetProtoChangeMarker(); + EXPECT_TRUE(!obj1Marker.IsProtoChangeMarker()); + + JSTaggedValue protoDetails2 = obj2Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails2.IsProtoChangeDetails()); + JSTaggedValue protoDetails1 = obj1Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails1.IsProtoChangeDetails()); + JSTaggedValue listeners1 = ProtoChangeDetails::Cast(protoDetails1.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners1 != JSTaggedValue(0)); + JSTaggedValue listeners2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners2 == JSTaggedValue(0)); + JSTaggedValue index = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetRegisterIndex(); + JSTaggedValue listenersResult = ChangeListener::Cast(listeners1.GetTaggedObject())->Get(index.GetArrayLength()); + EXPECT_TRUE(listenersResult == obj2Dynclass.GetTaggedValue()); +} + +HWTEST_F_L0(JSObjectTest, BuildRegisterTree) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle obj1 = JSObject::ObjectCreate(thread, nullHandle); + JSHandle obj2 = JSObject::ObjectCreate(thread, obj1); + JSHandle obj3 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj4 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj5 = JSObject::ObjectCreate(thread, obj4); + JSHandle obj6 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj7 = JSObject::ObjectCreate(thread, obj6); + + JSHandle obj1Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key1")); + JSHandle obj2Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key2")); + JSHandle obj3Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle obj4Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key4")); + JSHandle obj5Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key5")); + JSHandle obj6Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key6")); + JSHandle obj7Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key7")); + + JSHandle obj1Value(thread, JSTaggedValue(1)); + JSHandle obj2Value(thread, JSTaggedValue(2)); + JSHandle obj3Value(thread, JSTaggedValue(3)); + JSHandle obj4Value(thread, JSTaggedValue(4)); + JSHandle obj5Value(thread, JSTaggedValue(5)); + JSHandle obj6Value(thread, JSTaggedValue(6)); + JSHandle obj7Value(thread, JSTaggedValue(7)); + + JSObject::SetProperty(thread, JSHandle(obj1), obj1Key, obj1Value); + JSObject::SetProperty(thread, JSHandle(obj2), obj2Key, obj2Value); + JSObject::SetProperty(thread, JSHandle(obj3), obj3Key, obj3Value); + JSObject::SetProperty(thread, JSHandle(obj4), obj4Key, obj4Value); + JSObject::SetProperty(thread, JSHandle(obj5), obj5Key, obj5Value); + JSObject::SetProperty(thread, JSHandle(obj6), obj6Key, obj6Value); + JSObject::SetProperty(thread, JSHandle(obj7), obj7Key, obj7Value); + + JSHandle obj1Dynclass(thread, obj1->GetJSHClass()); + JSHandle obj2Dynclass(thread, obj2->GetJSHClass()); + JSHandle obj3Dynclass(thread, obj3->GetJSHClass()); + JSHandle obj4Dynclass(thread, obj4->GetJSHClass()); + JSHandle obj5Dynclass(thread, obj5->GetJSHClass()); + JSHandle obj6Dynclass(thread, obj6->GetJSHClass()); + JSHandle obj7Dynclass(thread, obj7->GetJSHClass()); + + JSHandle result3Marker = JSHClass::EnableProtoChangeMarker(thread, obj3Dynclass); + JSHandle result5Marker = JSHClass::EnableProtoChangeMarker(thread, obj5Dynclass); + EXPECT_TRUE(result3Marker->IsProtoChangeMarker()); + EXPECT_TRUE(!(ProtoChangeMarker::Cast(result3Marker->GetTaggedObject())->GetHasChanged())); + EXPECT_TRUE(result5Marker->IsProtoChangeMarker()); + EXPECT_TRUE(!(ProtoChangeMarker::Cast(result5Marker->GetTaggedObject())->GetHasChanged())); + + EXPECT_TRUE(obj4Dynclass->GetProtoChangeMarker().IsProtoChangeMarker()); + EXPECT_TRUE(!obj6Dynclass->GetProtoChangeMarker().IsProtoChangeMarker()); + + JSHandle result7Marker = JSHClass::EnableProtoChangeMarker(thread, obj7Dynclass); + EXPECT_TRUE(result7Marker->IsProtoChangeMarker()); + EXPECT_TRUE(!(ProtoChangeMarker::Cast(result7Marker->GetTaggedObject())->GetHasChanged())); + + JSTaggedValue protoDetails1 = obj1Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails1.IsProtoChangeDetails()); + JSTaggedValue listeners1Value = ProtoChangeDetails::Cast(protoDetails1.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners1Value != JSTaggedValue(0)); + JSHandle listeners1(thread, listeners1Value.GetTaggedObject()); + JSTaggedValue protoDetails2 = obj2Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails2.IsProtoChangeDetails()); + JSTaggedValue index2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetRegisterIndex(); + EXPECT_TRUE(listeners1->Get(index2.GetArrayLength()) == obj2Dynclass.GetTaggedValue()); + + JSTaggedValue listeners2Value = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners2Value != JSTaggedValue(0)); + JSHandle listeners2(thread, listeners2Value.GetTaggedObject()); + JSTaggedValue protoDetails4 = obj4Dynclass->GetProtoChangeDetails(); + JSTaggedValue protoDetails6 = obj6Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails4.IsProtoChangeDetails()); + EXPECT_TRUE(protoDetails6.IsProtoChangeDetails()); + JSTaggedValue index4 = ProtoChangeDetails::Cast(protoDetails4.GetTaggedObject())->GetRegisterIndex(); + EXPECT_TRUE(listeners2->Get(index4.GetArrayLength()) == obj4Dynclass.GetTaggedValue()); + JSTaggedValue index6 = ProtoChangeDetails::Cast(protoDetails6.GetTaggedObject())->GetRegisterIndex(); + EXPECT_TRUE(listeners2->Get(index6.GetArrayLength()) == obj6Dynclass.GetTaggedValue()); + + EXPECT_TRUE(listeners1->GetEnd() == 1); + EXPECT_TRUE(listeners2->GetEnd() == 2); +} + +HWTEST_F_L0(JSObjectTest, NoticeThroughChain) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle obj1 = JSObject::ObjectCreate(thread, nullHandle); + JSHandle obj2 = JSObject::ObjectCreate(thread, obj1); + JSHandle obj3 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj4 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj5 = JSObject::ObjectCreate(thread, obj4); + JSHandle obj6 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj7 = JSObject::ObjectCreate(thread, obj6); + + JSHandle obj1Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key1")); + JSHandle obj2Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key2")); + JSHandle obj3Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle obj4Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key4")); + JSHandle obj5Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key5")); + JSHandle obj6Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key6")); + JSHandle obj7Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key7")); + + JSHandle obj1Value(thread, JSTaggedValue(1)); + JSHandle obj2Value(thread, JSTaggedValue(2)); + JSHandle obj3Value(thread, JSTaggedValue(3)); + JSHandle obj4Value(thread, JSTaggedValue(4)); + JSHandle obj5Value(thread, JSTaggedValue(5)); + JSHandle obj6Value(thread, JSTaggedValue(6)); + JSHandle obj7Value(thread, JSTaggedValue(7)); + + JSObject::SetProperty(thread, JSHandle(obj1), obj1Key, obj1Value); + JSObject::SetProperty(thread, JSHandle(obj2), obj2Key, obj2Value); + JSObject::SetProperty(thread, JSHandle(obj3), obj3Key, obj3Value); + JSObject::SetProperty(thread, JSHandle(obj4), obj4Key, obj4Value); + JSObject::SetProperty(thread, JSHandle(obj5), obj5Key, obj5Value); + JSObject::SetProperty(thread, JSHandle(obj6), obj6Key, obj6Value); + JSObject::SetProperty(thread, JSHandle(obj7), obj7Key, obj7Value); + + JSHandle obj1Dynclass(thread, obj1->GetJSHClass()); + JSHandle obj2Dynclass(thread, obj2->GetJSHClass()); + JSHandle obj3Dynclass(thread, obj3->GetJSHClass()); + JSHandle obj4Dynclass(thread, obj4->GetJSHClass()); + JSHandle obj5Dynclass(thread, obj5->GetJSHClass()); + JSHandle obj6Dynclass(thread, obj6->GetJSHClass()); + JSHandle obj7Dynclass(thread, obj7->GetJSHClass()); + + JSHClass::EnableProtoChangeMarker(thread, obj3Dynclass); + JSHClass::EnableProtoChangeMarker(thread, obj7Dynclass); + JSHClass::EnableProtoChangeMarker(thread, obj5Dynclass); + + JSHClass::NoticeThroughChain(thread, obj2Dynclass); + JSHClass::UnregisterOnProtoChain(thread, obj2Dynclass); + JSTaggedValue protoDetails1 = obj1Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails1.IsProtoChangeDetails()); + JSTaggedValue listeners1Value = ProtoChangeDetails::Cast(protoDetails1.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners1Value != JSTaggedValue(0)); + JSHandle listeners1(thread, listeners1Value.GetTaggedObject()); + array_size_t holeIndex = ChangeListener::CheckHole(listeners1); + EXPECT_TRUE(holeIndex == 0); + + JSTaggedValue protoDetails2 = obj2Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails2.IsProtoChangeDetails()); + JSTaggedValue listeners2Value = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners2Value != JSTaggedValue(0)); + JSTaggedValue index2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetRegisterIndex(); + EXPECT_TRUE(listeners1->Get(index2.GetArrayLength()) == JSTaggedValue::Hole()); + + JSTaggedValue obj6Marker = obj6Dynclass->GetProtoChangeMarker(); + EXPECT_TRUE(obj6Marker.IsProtoChangeMarker()); + bool hasChanged6 = ProtoChangeMarker::Cast(obj6Marker.GetTaggedObject())->GetHasChanged(); + EXPECT_TRUE(hasChanged6); + + JSTaggedValue obj4Marker = obj4Dynclass->GetProtoChangeMarker(); + EXPECT_TRUE(obj4Marker.IsProtoChangeMarker()); + bool hasChanged4 = ProtoChangeMarker::Cast(obj4Marker.GetTaggedObject())->GetHasChanged(); + EXPECT_TRUE(hasChanged4); +} + +HWTEST_F_L0(JSObjectTest, ChangeProtoAndNoticeTheChain) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle obj1 = JSObject::ObjectCreate(thread, nullHandle); + JSHandle obj2 = JSObject::ObjectCreate(thread, obj1); + JSHandle obj3 = JSObject::ObjectCreate(thread, obj1); + JSHandle obj4 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj5 = JSObject::ObjectCreate(thread, obj4); + JSHandle obj6 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj7 = JSObject::ObjectCreate(thread, obj6); + + JSHandle obj1Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key1")); + JSHandle obj2Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key2")); + JSHandle obj3Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle obj4Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key4")); + JSHandle obj5Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key5")); + JSHandle obj6Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key6")); + JSHandle obj7Key(thread->GetEcmaVM()->GetFactory()->NewFromString("key7")); + + JSHandle obj1Value(thread, JSTaggedValue(1)); + JSHandle obj2Value(thread, JSTaggedValue(2)); + JSHandle obj3Value(thread, JSTaggedValue(3)); + JSHandle obj4Value(thread, JSTaggedValue(4)); + JSHandle obj5Value(thread, JSTaggedValue(5)); + JSHandle obj6Value(thread, JSTaggedValue(6)); + JSHandle obj7Value(thread, JSTaggedValue(7)); + + JSObject::SetProperty(thread, JSHandle(obj1), obj1Key, obj1Value); + JSObject::SetProperty(thread, JSHandle(obj2), obj2Key, obj2Value); + JSObject::SetProperty(thread, JSHandle(obj3), obj3Key, obj3Value); + JSObject::SetProperty(thread, JSHandle(obj4), obj4Key, obj4Value); + JSObject::SetProperty(thread, JSHandle(obj5), obj5Key, obj5Value); + JSObject::SetProperty(thread, JSHandle(obj6), obj6Key, obj6Value); + JSObject::SetProperty(thread, JSHandle(obj7), obj7Key, obj7Value); + + JSHandle obj5Dynclass(thread, obj5->GetJSHClass()); + JSHandle obj7Dynclass(thread, obj7->GetJSHClass()); + + JSHClass::EnableProtoChangeMarker(thread, obj7Dynclass); + JSHClass::EnableProtoChangeMarker(thread, obj5Dynclass); + + JSObject::SetPrototype(thread, obj2, JSHandle(obj3)); + + JSHandle obj1Dynclass(thread, obj1->GetJSHClass()); + JSHandle obj2Dynclass(thread, obj2->GetJSHClass()); + JSHandle obj3Dynclass(thread, obj3->GetJSHClass()); + JSHandle obj4Dynclass(thread, obj4->GetJSHClass()); + JSHandle obj6Dynclass(thread, obj6->GetJSHClass()); + + JSTaggedValue obj6Marker = obj6Dynclass->GetProtoChangeMarker(); + EXPECT_TRUE(obj6Marker.IsProtoChangeMarker()); + bool hasChanged6 = ProtoChangeMarker::Cast(obj6Marker.GetTaggedObject())->GetHasChanged(); + EXPECT_TRUE(hasChanged6); + + JSTaggedValue obj4Marker = obj4Dynclass->GetProtoChangeMarker(); + EXPECT_TRUE(obj4Marker.IsProtoChangeMarker()); + bool hasChanged4 = ProtoChangeMarker::Cast(obj4Marker.GetTaggedObject())->GetHasChanged(); + EXPECT_TRUE(hasChanged4); + + JSTaggedValue protoDetails1 = obj1Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails1.IsProtoChangeDetails()); + JSTaggedValue protoDetails2 = obj2Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails2.IsProtoChangeDetails()); + JSTaggedValue protoDetails3 = obj3Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails3.IsProtoChangeDetails()); + JSTaggedValue protoDetails4 = obj4Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails4.IsProtoChangeDetails()); + JSTaggedValue protoDetails6 = obj6Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails6.IsProtoChangeDetails()); + + JSTaggedValue listeners1 = ProtoChangeDetails::Cast(protoDetails1.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners1 != JSTaggedValue(0)); + JSTaggedValue listeners2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners2 != JSTaggedValue(0)); + JSTaggedValue listeners3 = ProtoChangeDetails::Cast(protoDetails3.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners3 != JSTaggedValue(0)); + + JSTaggedValue index2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetRegisterIndex(); + JSTaggedValue index3 = ProtoChangeDetails::Cast(protoDetails3.GetTaggedObject())->GetRegisterIndex(); + JSTaggedValue index4 = ProtoChangeDetails::Cast(protoDetails4.GetTaggedObject())->GetRegisterIndex(); + JSTaggedValue index6 = ProtoChangeDetails::Cast(protoDetails6.GetTaggedObject())->GetRegisterIndex(); + + JSTaggedValue result2 = ChangeListener::Cast(listeners3.GetTaggedObject())->Get(index2.GetArrayLength()); + JSTaggedValue result3 = ChangeListener::Cast(listeners1.GetTaggedObject())->Get(index3.GetArrayLength()); + JSTaggedValue result4 = ChangeListener::Cast(listeners2.GetTaggedObject())->Get(index4.GetArrayLength()); + JSTaggedValue result6 = ChangeListener::Cast(listeners2.GetTaggedObject())->Get(index6.GetArrayLength()); + + EXPECT_TRUE(result2 == obj2Dynclass.GetTaggedValue()); + EXPECT_TRUE(result3 == obj3Dynclass.GetTaggedValue()); + EXPECT_TRUE(result4 == obj4Dynclass.GetTaggedValue()); + EXPECT_TRUE(result6 == obj6Dynclass.GetTaggedValue()); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_primitive_ref_test.cpp b/ecmascript/tests/js_primitive_ref_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..012404989573eef978dfd7ca79822e23e7663dd8 --- /dev/null +++ b/ecmascript/tests/js_primitive_ref_test.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/object_operator.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JSPrimitiveRefTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(JSPrimitiveRefTest, StringCreate) +{ + JSHandle hello(thread->GetEcmaVM()->GetFactory()->NewFromString("hello")); + JSHandle str(JSPrimitiveRef::StringCreate(thread, hello)); + + JSHandle idx(thread->GetEcmaVM()->GetFactory()->NewFromString("0")); + bool status = JSPrimitiveRef::HasProperty(thread, str, idx); + ASSERT_TRUE(status); + + PropertyDescriptor desc(thread); + status = JSPrimitiveRef::GetOwnProperty(thread, str, idx, desc); + ASSERT_TRUE(status); + JSHandle h = thread->GetEcmaVM()->GetFactory()->NewFromString("h"); + JSHandle h2 = JSTaggedValue::ToString(thread, desc.GetValue()); + ASSERT_TRUE(h->Compare(*h2) == 0); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_promise_test.cpp b/ecmascript/tests/js_promise_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c93a6c13fee8c3175ab51339081e6d6b1f493c98 --- /dev/null +++ b/ecmascript/tests/js_promise_test.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tests/test_helper.h" + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_promise.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; + +namespace panda::test { +class JSPromiseTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + JSThread *thread {nullptr}; + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; +}; + +HWTEST_F_L0(JSPromiseTest, CreateResolvingFunctions) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle promiseFunc = env->GetPromiseFunction(); + JSHandle jsPromise = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(promiseFunc), promiseFunc)); + JSHandle reactions = JSPromise::CreateResolvingFunctions(thread, jsPromise); + JSHandle resolve(thread, reactions->GetResolveFunction()); + JSHandle reject(thread, reactions->GetRejectFunction()); + EXPECT_EQ(resolve->IsCallable(), true); + EXPECT_EQ(reject->IsCallable(), true); +} + +HWTEST_F_L0(JSPromiseTest, NewPromiseCapability) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle promise = env->GetPromiseFunction(); + + JSHandle capbility = JSPromise::NewPromiseCapability(thread, promise); + JSHandle newPromise(thread, capbility->GetPromise()); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + + JSHandle resolve(thread, capbility->GetResolve()); + JSHandle reject(thread, capbility->GetReject()); + EXPECT_EQ(resolve.GetTaggedValue().IsCallable(), true); + EXPECT_EQ(resolve.GetTaggedValue().IsCallable(), true); + + JSHandle resolve_promise(thread, resolve->GetPromise()); + JSHandle reject_promise(thread, reject->GetPromise()); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise.GetTaggedValue(), resolve_promise.GetTaggedValue()), true); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise.GetTaggedValue(), reject_promise.GetTaggedValue()), true); +} + +HWTEST_F_L0(JSPromiseTest, FullFillPromise) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + JSHandle promise = env->GetPromiseFunction(); + JSHandle capbility = JSPromise::NewPromiseCapability(thread, promise); + JSHandle newPromise(thread, capbility->GetPromise()); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(newPromise->GetPromiseResult().IsUndefined(), true); + + array_size_t length = 1; + JSHandle array = factory->NewTaggedArray(length); + array->Set(thread, 0, JSTaggedValue(33)); + JSHandle resolve(thread, capbility->GetResolve()); + JSHandle undefined(thread, JSTaggedValue::Undefined()); + JSFunction::Call(thread, resolve, undefined, array); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise->GetPromiseResult(), JSTaggedValue(33)), true); +} + +HWTEST_F_L0(JSPromiseTest, RejectPromise) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + JSHandle promise = env->GetPromiseFunction(); + JSHandle capbility = JSPromise::NewPromiseCapability(thread, promise); + JSHandle newPromise(thread, capbility->GetPromise()); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(newPromise->GetPromiseResult().IsUndefined(), true); + + array_size_t length = 1; + JSHandle array = factory->NewTaggedArray(length); + array->Set(thread, 0, JSTaggedValue(44)); + JSHandle reject(thread, capbility->GetReject()); + JSHandle undefined(thread, JSTaggedValue::Undefined()); + JSFunction::Call(thread, reject, undefined, array); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise->GetPromiseResult(), JSTaggedValue(44)), true); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_proxy_test.cpp b/ecmascript/tests/js_proxy_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eaf16495beda4cbfbcb6f23b032922989e2fd6d8 --- /dev/null +++ b/ecmascript/tests/js_proxy_test.cpp @@ -0,0 +1,641 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_proxy.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; + +namespace panda::test { +class JSProxyTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +static JSFunction *JSObjectTestCreate(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + return globalEnv->GetObjectFunction().GetObject(); +} + +HWTEST_F_L0(JSProxyTest, ProxyCreate) +{ + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(dynclass), dynclass)); + + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, targetHandle, key, value); + EXPECT_EQ(JSObject::GetProperty(thread, targetHandle, key).GetValue()->GetInt(), 1); + + JSHandle handlerHandle( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + + EXPECT_EQ(JSProxy::GetProperty(thread, proxyHandle, key).GetValue()->GetInt(), 1); + PropertyDescriptor desc(thread); + JSProxy::GetOwnProperty(thread, proxyHandle, key, desc); + EXPECT_EQ(desc.GetValue()->GetInt(), 1); +} + +// ES6 9.5.8 [[Get]] (P, Receiver) +// Called by the following function +JSTaggedValue HandlerGetProperty([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(10); // 10 : test case +} + +HWTEST_F_L0(JSProxyTest, GetProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "get" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, targetHandle, key, value); + EXPECT_EQ(JSObject::GetProperty(thread, targetHandle, key).GetValue()->GetInt(), 1); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + + EXPECT_EQ(JSProxy::GetProperty(thread, proxyHandle, key).GetValue()->GetInt(), 1); + + // 2. handler has "get" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle getKey = thread->GlobalConstants()->GetHandledGetString(); + JSHandle getHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerGetProperty))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), getKey, getHandle); + + JSHandle proxyHandle2(JSProxy::ProxyCreate(thread, targetHandle, handlerHandle)); + EXPECT_TRUE(*proxyHandle2 != nullptr); + JSHandle key2(factory->NewFromString("y")); + EXPECT_EQ(JSProxy::GetProperty(thread, proxyHandle2, key2).GetValue()->GetInt(), 10); +} + +// ES6 9.5.5 [[GetOwnProperty]] (P) +// Called by the following function +JSTaggedValue HandlerGetOwnProperty([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::Undefined()); +} + +HWTEST_F_L0(JSProxyTest, GetOwnProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "get" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, targetHandle, key, value); + EXPECT_EQ(JSObject::GetProperty(thread, targetHandle, key).GetValue()->GetInt(), 1); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + + PropertyDescriptor desc(thread); + JSProxy::GetOwnProperty(thread, proxyHandle, key, desc); + EXPECT_EQ(desc.GetValue()->GetInt(), 1); + + // 2. handler has "get" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle defineKey = thread->GlobalConstants()->GetHandledGetOwnPropertyDescriptorString(); + JSHandle defineHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerGetOwnProperty))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), defineKey, defineHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + JSHandle key2(factory->NewFromString("y")); + PropertyDescriptor desc2(thread); + EXPECT_FALSE(JSProxy::GetOwnProperty(thread, proxyHandle2, key2, desc2)); +} + +// ES6 9.5.9 [[Set]] ( P, V, Receiver) +JSTaggedValue HandlerSetProperty([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +HWTEST_F_L0(JSProxyTest, SetProperty) +{ + // 1. handler has no "get" + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "get" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + + EXPECT_TRUE(JSProxy::SetProperty(thread, proxyHandle, key, value)); + EXPECT_EQ(JSProxy::GetProperty(thread, proxyHandle, key).GetValue()->GetInt(), 1); + EXPECT_EQ(JSObject::GetProperty(thread, targetHandle, key).GetValue()->GetInt(), 1); + + // 2. handler has "set" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle setKey = thread->GlobalConstants()->GetHandledSetString(); + JSHandle setHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerSetProperty))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), setKey, setHandle); + + JSHandle proxyHandle2(JSProxy::ProxyCreate(thread, targetHandle, handlerHandle)); + EXPECT_TRUE(*proxyHandle2 != nullptr); + JSHandle value2(thread, JSTaggedValue(10)); + EXPECT_FALSE(JSProxy::SetProperty(thread, proxyHandle2, key, value2)); + EXPECT_EQ(JSProxy::GetProperty(thread, proxyHandle2, key).GetValue()->GetInt(), 1); +} + +// ES6 9.5.6 [[DefineOwnProperty]] (P, Desc) +JSTaggedValue HandlerDefineOwnProperty([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +HWTEST_F_L0(JSProxyTest, DefineOwnProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "defineProperty" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(1))); + EXPECT_TRUE(JSProxy::DefineOwnProperty(thread, proxyHandle, key, desc)); + EXPECT_EQ(JSProxy::GetProperty(thread, proxyHandle, key).GetValue()->GetInt(), 1); + EXPECT_EQ(JSObject::GetProperty(thread, targetHandle, key).GetValue()->GetInt(), 1); + + // 2. handler has "defineProperty" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle setKey = thread->GlobalConstants()->GetHandledDefinePropertyString(); + JSHandle setHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerDefineOwnProperty))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), setKey, setHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(10))); + EXPECT_FALSE(JSProxy::DefineOwnProperty(thread, proxyHandle, key, desc2)); + EXPECT_EQ(JSProxy::GetProperty(thread, proxyHandle2, key).GetValue()->GetInt(), 1); +} + +JSTaggedValue HandlerDeleteProperty([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +// ES6 9.5.10 [[Delete]] (P) +HWTEST_F_L0(JSProxyTest, DeleteProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "deleteProperty" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + EXPECT_TRUE(JSProxy::DefineOwnProperty(thread, proxyHandle, key, desc)); + EXPECT_EQ(JSProxy::GetProperty(thread, proxyHandle, key).GetValue()->GetInt(), 1); + EXPECT_EQ(JSObject::GetProperty(thread, targetHandle, key).GetValue()->GetInt(), 1); + EXPECT_TRUE(JSProxy::DeleteProperty(thread, proxyHandle, key)); + PropertyDescriptor resDesc(thread); + JSProxy::GetOwnProperty(thread, proxyHandle, key, resDesc); + EXPECT_TRUE(JSTaggedValue::SameValue(resDesc.GetValue().GetTaggedValue(), JSTaggedValue::Undefined())); + + // 2. handler has "deleteProperty" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle funcKey = thread->GlobalConstants()->GetHandledDeletePropertyString(); + JSHandle funcHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerDeleteProperty))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), funcKey, funcHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + EXPECT_TRUE(JSProxy::DefineOwnProperty(thread, proxyHandle2, key, desc2)); + EXPECT_EQ(JSProxy::GetProperty(thread, proxyHandle2, key).GetValue()->GetInt(), 1); + EXPECT_FALSE(JSProxy::DeleteProperty(thread, proxyHandle2, key)); +} + +JSTaggedValue HandlerGetPrototype([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::Null()); +} + +// ES6 9.5.1 [[GetPrototypeOf]] ( ) +HWTEST_F_L0(JSProxyTest, GetPrototypeOf) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "GetPrototypeOf" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle proto(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + JSHandle targetHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(targetHandle->IsECMAObject()); + JSObject::SetPrototype(thread, JSHandle(targetHandle), proto); + EXPECT_TRUE( + JSTaggedValue::SameValue(JSHandle(targetHandle)->GetPrototype(thread), proto.GetTaggedValue())); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + + EXPECT_TRUE(JSTaggedValue::SameValue(JSProxy::GetPrototype(thread, proxyHandle), proto.GetTaggedValue())); + + // 2. handler has "GetPrototypeOf" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle funcKey = thread->GlobalConstants()->GetHandledGetPrototypeOfString(); + JSHandle funcHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerGetPrototype))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), funcKey, funcHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + EXPECT_TRUE(JSTaggedValue::SameValue(JSProxy::GetPrototype(thread, proxyHandle2), JSTaggedValue::Null())); +} + +JSTaggedValue HandlerSetPrototype([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +// ES6 9.5.2 [[SetPrototypeOf]] (V) +HWTEST_F_L0(JSProxyTest, SetPrototypeOf) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "SetPrototypeOf" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle proto(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + JSHandle targetHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + + JSProxy::SetPrototype(thread, proxyHandle, proto); + EXPECT_TRUE( + JSTaggedValue::SameValue(JSHandle(targetHandle)->GetPrototype(thread), proto.GetTaggedValue())); + + // 2. handler has "SetPrototypeOf" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle funcKey = thread->GlobalConstants()->GetHandledSetPrototypeOfString(); + JSHandle funcHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerSetPrototype))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), funcKey, funcHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + EXPECT_FALSE(JSProxy::SetPrototype(thread, proxyHandle2, proto)); +} + +JSTaggedValue HandlerIsExtensible([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +// ES6 9.5.3 [[IsExtensible]] ( ) +HWTEST_F_L0(JSProxyTest, IsExtensible) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "IsExtensible" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + + bool status1 = JSProxy::IsExtensible(thread, proxyHandle); + bool status2 = JSHandle::Cast(targetHandle)->IsExtensible(); + EXPECT_TRUE(status1 == status2); + + // 2. handler has "IsExtensible" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle funcKey = thread->GlobalConstants()->GetHandledIsExtensibleString(); + JSHandle funcHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerIsExtensible))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), funcKey, funcHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + EXPECT_FALSE(JSProxy::IsExtensible(thread, proxyHandle2)); +} + +JSTaggedValue HandlerPreventExtensions([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +// ES6 9.5.4 [[PreventExtensions]] ( ) +HWTEST_F_L0(JSProxyTest, PreventExtensions) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "PreventExtensions" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + + bool status1 = JSProxy::PreventExtensions(thread, proxyHandle); + EXPECT_TRUE(status1); + bool status2 = JSHandle::Cast(targetHandle)->IsExtensible(); + EXPECT_FALSE(status2); + + // 2. handler has "PreventExtensions" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle funcKey = thread->GlobalConstants()->GetHandledPreventExtensionsString(); + JSHandle funcHandle( + factory->NewJSFunction(env, reinterpret_cast(HandlerPreventExtensions))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), funcKey, funcHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + EXPECT_FALSE(JSProxy::PreventExtensions(thread, proxyHandle2)); +} + +JSTaggedValue HandlerHasProperty([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +// ES6 9.5.7 [[HasProperty]] (P) +HWTEST_F_L0(JSProxyTest, HasProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "HasProperty" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(1))); + JSObject::DefineOwnProperty(thread, JSHandle::Cast(targetHandle), key, desc); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + + EXPECT_TRUE(JSProxy::HasProperty(thread, proxyHandle, key)); + + // 2. handler has "HasProperty" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle funcKey = thread->GlobalConstants()->GetHandledHasString(); + JSHandle funcHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerHasProperty))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), funcKey, funcHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + EXPECT_FALSE(JSProxy::HasProperty(thread, proxyHandle2, key)); +} + +JSTaggedValue HandlerOwnPropertyKeys([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + auto thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle arr = factory->NewJSArray(); + return JSTaggedValue(arr.GetTaggedValue()); +} + +// ES6 9.5.12 [[OwnPropertyKeys]] () +HWTEST_F_L0(JSProxyTest, OwnPropertyKeys) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "OwnPropertyKeys" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(1))); + JSObject::DefineOwnProperty(thread, JSHandle::Cast(targetHandle), key, desc); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + JSHandle res = JSProxy::OwnPropertyKeys(thread, proxyHandle); + + EXPECT_TRUE(JSTaggedValue::SameValue(res->Get(0), key.GetTaggedValue())); + + // 2. handler has "OwnPropertyKeys" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle funcKey = thread->GlobalConstants()->GetHandledOwnKeysString(); + JSHandle funcHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerOwnPropertyKeys))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), funcKey, funcHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + JSHandle res2 = JSProxy::OwnPropertyKeys(thread, proxyHandle2); + EXPECT_TRUE(res2->GetLength() == 0 || !JSTaggedValue::SameValue(res2->Get(0), key.GetTaggedValue())); +} + +JSTaggedValue HandlerCall([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} +JSTaggedValue HandlerFunction([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::True()); +} + +// ES6 9.5.13 [[Call]] (thisArgument, argumentsList) +HWTEST_F_L0(JSProxyTest, Call) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // 1. handler has no "Call" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerFunction))); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + JSHandle arg = factory->EmptyArray(); + JSTaggedValue res = JSProxy::CallInternal(thread, proxyHandle, JSHandle::Cast(proxyHandle), arg); + JSHandle taggedRes(thread, res); + + EXPECT_TRUE(JSTaggedValue::SameValue(taggedRes.GetTaggedValue(), JSTaggedValue::True())); + + // 2. handler has "Call" + JSHandle funcKey = thread->GlobalConstants()->GetHandledApplyString(); + JSHandle funcHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerCall))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), funcKey, funcHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + JSHandle arg2 = factory->EmptyArray(); + JSTaggedValue res2 = + JSProxy::CallInternal(thread, proxyHandle2, JSHandle::Cast(proxyHandle2), arg2); + JSHandle taggedRes2(thread, res2); + + EXPECT_TRUE(JSTaggedValue::SameValue(taggedRes2.GetTaggedValue(), JSTaggedValue::False())); +} + +JSTaggedValue HandlerConstruct([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + auto thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + + JSHandle key(factory->NewFromString("x")); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(2))); // 2 : test case + JSObject::DefineOwnProperty(argv->GetThread(), JSHandle::Cast(obj), key, desc); + return JSTaggedValue(obj.GetTaggedValue()); +} +JSTaggedValue HandlerConFunc([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + auto thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + + JSHandle key(factory->NewFromString("x")); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(1))); + JSObject::DefineOwnProperty(argv->GetThread(), JSHandle::Cast(obj), key, desc); + return JSTaggedValue(obj.GetTaggedValue()); +} + +// ES6 9.5.14 [[Construct]] ( argumentsList, newTarget) +HWTEST_F_L0(JSProxyTest, Construct) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // 1. handler has no "Construct" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerConFunc))); + JSHandle::Cast(targetHandle)->GetJSHClass()->SetConstructor(true); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + JSHandle arg = factory->EmptyArray(); + JSTaggedValue res = JSProxy::ConstructInternal(thread, proxyHandle, arg, targetHandle); + JSHandle taggedRes(thread, res); + JSHandle key(factory->NewFromString("x")); + EXPECT_EQ(JSObject::GetProperty(thread, taggedRes, key).GetValue()->GetInt(), 1); + + // 2. handler has "Construct" + JSHandle funcKey = thread->GlobalConstants()->GetHandledProxyConstructString(); + JSHandle funcHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerConstruct))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), funcKey, funcHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + JSHandle arg2 = factory->EmptyArray(); + JSTaggedValue res2 = JSProxy::ConstructInternal(thread, proxyHandle2, arg2, targetHandle); + JSHandle taggedRes2(thread, res2); + EXPECT_EQ(JSObject::GetProperty(thread, taggedRes2, key).GetValue()->GetInt(), 2); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_serializer_test.cpp b/ecmascript/tests/js_serializer_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bbe607a7f2b27e9a6bd981baaa70273ab684d5d8 --- /dev/null +++ b/ecmascript/tests/js_serializer_test.cpp @@ -0,0 +1,944 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ecmascript/builtins/builtins_arraybuffer.h" +#include "ecmascript/ecma_language_context.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_float32_array.h" +#include "ecmascript/js_float64_array.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_int16_array.h" +#include "ecmascript/js_int32_array.h" +#include "ecmascript/js_int8_array.h" +#include "ecmascript/js_regexp.h" +#include "ecmascript/js_serializer.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/js_uint16_array.h" +#include "ecmascript/js_uint32_array.h" +#include "ecmascript/js_uint8_array.h" +#include "ecmascript/js_uint8_clamped_array.h" +#include "ecmascript/linked_hash_table-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace testing::ext; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class JSDeserializerTest { +public: + JSDeserializerTest() : ecmaVm(nullptr), scope(nullptr), thread(nullptr) {} + void Init() + { + RuntimeOptions options; + options.SetShouldLoadBootPandaFiles(false); + options.SetShouldInitializeIntrinsics(false); + options.SetBootIntrinsicSpaces({"ecmascript"}); + options.SetBootClassSpaces({"ecmascript"}); + options.SetRuntimeType("ecmascript"); + ecmaVm = EcmaVM::Create(options); + EXPECT_TRUE(ecmaVm != nullptr) << "Cannot create Runtime"; + thread = ecmaVm->GetJSThread(); + scope = new EcmaHandleScope(thread); + thread->SetIsEcmaInterpreter(true); + ecmaVm->GetFactory()->SetTriggerGc(true); + } + void Destroy() + { + delete scope; + ecmaVm->GetFactory()->SetTriggerGc(false); + auto thread = ecmaVm->GetJSThread(); + thread->ClearException(); + [[maybe_unused]] bool success = EcmaVM::Destroy(ecmaVm); + EXPECT_TRUE(success) << "Cannot destroy Runtime"; + } + void JSSpecialValueTest(std::pair data) + { + Init(); + JSHandle jsTrue(thread, JSTaggedValue::True()); + JSHandle jsFalse(thread, JSTaggedValue::False()); + JSHandle jsUndefined(thread, JSTaggedValue::Undefined()); + JSHandle jsNull(thread, JSTaggedValue::Null()); + JSHandle jsHole(thread, JSTaggedValue::Hole()); + + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle retTrue = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(JSTaggedValue::SameValue(jsTrue, retTrue)) << "Not same value for JS_TRUE"; + JSHandle retFalse = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(JSTaggedValue::SameValue(jsFalse, retFalse)) << "Not same value for JS_FALSE"; + JSHandle retUndefined = deserializer.DeserializeJSTaggedValue(); + JSHandle retNull = deserializer.DeserializeJSTaggedValue(); + JSHandle retHole = deserializer.DeserializeJSTaggedValue(); + + EXPECT_TRUE(JSTaggedValue::SameValue(jsUndefined, retUndefined)) << "Not same value for JS_UNDEFINED"; + EXPECT_TRUE(JSTaggedValue::SameValue(jsNull, retNull)) << "Not same value for JS_NULL"; + EXPECT_TRUE(JSTaggedValue::SameValue(jsHole, retHole)) << "Not same value for JS_HOLE"; + Destroy(); + } + + void JSPlainObjectTest(std::pair data) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle objValue1 = deserializer.DeserializeJSTaggedValue(); + JSHandle objValue2 = deserializer.DeserializeJSTaggedValue(); + + JSHandle retObj1 = JSHandle::Cast(objValue1); + JSHandle retObj2 = JSHandle::Cast(objValue2); + EXPECT_FALSE(retObj1.IsEmpty()); + EXPECT_FALSE(retObj2.IsEmpty()); + + JSHandle array1 = JSObject::GetOwnPropertyKeys(thread, retObj1); + int length1 = array1->GetLength(); + EXPECT_EQ(length1, 4); // 4 : test case + double sum1 = 0.0; + for (int i = 0; i < length1; i++) { + JSHandle key(thread, array1->Get(i)); + double a = JSObject::GetProperty(thread, JSHandle(retObj1), key).GetValue()->GetNumber(); + sum1 += a; + } + EXPECT_EQ(sum1, 10); // 10 : test case + + JSHandle array2 = JSObject::GetOwnPropertyKeys(thread, retObj2); + int length2 = array2->GetLength(); + EXPECT_EQ(length2, 4); // 4 : test case + double sum2 = 0.0; + for (int i = 0; i < length2; i++) { + JSHandle key(thread, array2->Get(i)); + double a = JSObject::GetProperty(thread, JSHandle(retObj2), key).GetValue()->GetNumber(); + sum2 += a; + } + EXPECT_EQ(sum2, 26); // 26 : test case + Destroy(); + } + + void DescriptionTest(std::pair data) + { + Init(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle key1(factory->NewFromString("x")); + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromString("y")); + JSHandle value1(thread, JSTaggedValue(1)); + JSHandle value2(thread, JSTaggedValue(2)); // 2 : test case + + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle objValue = deserializer.DeserializeJSTaggedValue(); + JSHandle retObj = JSHandle::Cast(objValue); + + PropertyDescriptor desc3(thread); + PropertyDescriptor desc4(thread); + + JSHandle array1 = JSObject::GetOwnPropertyKeys(thread, retObj); + JSHandle retKey1(thread, array1->Get(0)); + JSHandle retKey2(thread, array1->Get(1)); + JSObject::GetOwnProperty(thread, retObj, retKey1, desc3); + JSObject::GetOwnProperty(thread, retObj, retKey2, desc4); + EXPECT_EQ(key1.GetTaggedValue().GetRawData(), retKey1.GetTaggedValue().GetRawData()); + + EXPECT_EQ(desc3.GetValue().GetTaggedValue().GetRawData(), value1.GetTaggedValue().GetRawData()); + EXPECT_TRUE(desc3.IsWritable()); + EXPECT_FALSE(desc3.IsEnumerable()); + EXPECT_TRUE(desc3.IsConfigurable()); + + EXPECT_EQ(desc4.GetValue().GetTaggedValue().GetRawData(), value2.GetTaggedValue().GetRawData()); + EXPECT_FALSE(desc4.IsWritable()); + EXPECT_TRUE(desc4.IsEnumerable()); + EXPECT_FALSE(desc4.IsConfigurable()); + Destroy(); + } + + void JSSetTest(std::pair data) + { + Init(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle value1(thread, JSTaggedValue(7)); // 7 : test case + JSHandle value2(thread, JSTaggedValue(9)); // 9 : test case + JSHandle value3(factory->NewFromString("x")); + JSHandle value4(factory->NewFromString("y")); + + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle setValue = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!setValue.IsEmpty()); + JSHandle retSet = JSHandle::Cast(setValue); + JSHandle array = JSObject::GetOwnPropertyKeys(thread, JSHandle::Cast(retSet)); + int propertyLength = array->GetLength(); + EXPECT_EQ(propertyLength, 2); // 2 : test case + int sum = 0; + for (int i = 0; i < propertyLength; i++) { + JSHandle key(thread, array->Get(i)); + double a = JSObject::GetProperty(thread, JSHandle(retSet), key).GetValue()->GetNumber(); + sum += a; + } + EXPECT_EQ(sum, 16); // 16 : test case + + EXPECT_EQ(retSet->GetSize(), 4); // 4 : test case + EXPECT_TRUE(retSet->Has(value1.GetTaggedValue())); + EXPECT_TRUE(retSet->Has(value2.GetTaggedValue())); + EXPECT_TRUE(retSet->Has(value3.GetTaggedValue())); + EXPECT_TRUE(retSet->Has(value4.GetTaggedValue())); + Destroy(); + } + + void JSArrayTest(std::pair data) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle arrayValue = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!arrayValue.IsEmpty()); + + JSHandle retArray = JSHandle::Cast(arrayValue); + + JSHandle keyArray = JSObject::GetOwnPropertyKeys(thread, JSHandle(retArray)); + int propertyLength = keyArray->GetLength(); + EXPECT_EQ(propertyLength, 23); // 23 : test case + int sum = 0; + for (int i = 0; i < propertyLength; i++) { + JSHandle key(thread, keyArray->Get(i)); + double a = JSObject::GetProperty(thread, JSHandle(retArray), key).GetValue()->GetNumber(); + sum += a; + } + EXPECT_EQ(sum, 226); // 226 : test case + + // test get value from array + for (int i = 0; i < 20; i++) { // 20 : test case + JSHandle data = JSArray::FastGetPropertyByValue(thread, arrayValue, i); + EXPECT_EQ(i, data.GetTaggedValue().GetInt()); + } + Destroy(); + } + + void ObjectsPropertyReferenceTest(std::pair data) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle objValue1 = deserializer.DeserializeJSTaggedValue(); + JSHandle objValue2 = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!objValue1.IsEmpty()) << "[Empty] Deserialize obj1 fail"; + EXPECT_TRUE(!objValue2.IsEmpty()) << "[Empty] Deserialize obj2 fail"; + Destroy(); + } + + void EcmaStringTest(std::pair data) + { + Init(); + const char *rawStr = "this is a test ecmaString"; + JSHandle ecmaString = thread->GetEcmaVM()->GetFactory()->NewFromString(rawStr); + + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize ecmaString fail"; + EXPECT_TRUE(res->IsString()) << "[NotString] Deserialize ecmaString fail"; + JSHandle resEcmaString = JSHandle::Cast(res); + EXPECT_TRUE(ecmaString->GetHashcode() == resEcmaString->GetHashcode()) << "Not same HashCode"; + EXPECT_TRUE(EcmaString::StringsAreEqual(*ecmaString, *resEcmaString)) << "Not same EcmaString"; + Destroy(); + } + + void Int32Test(std::pair data) + { + Init(); + int32_t a = 64; + int32_t min = -2147483648; + int32_t b = -63; + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle resA = deserializer.DeserializeJSTaggedValue(); + JSHandle resMin = deserializer.DeserializeJSTaggedValue(); + JSHandle resB = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!resA.IsEmpty() && !resMin.IsEmpty() && !resB.IsEmpty()) << "[Empty] Deserialize Int32 fail"; + EXPECT_TRUE(resA->IsInt() && resMin->IsInt() && resB->IsInt()) << "[NotInt] Deserialize Int32 fail"; + EXPECT_TRUE(JSTaggedValue::ToInt32(thread, resA) == a) << "Not Same Value"; + EXPECT_TRUE(JSTaggedValue::ToInt32(thread, resMin) == min) << "Not Same Value"; + EXPECT_TRUE(JSTaggedValue::ToInt32(thread, resB) == b) << "Not Same Value"; + Destroy(); + } + + void DoubleTest(std::pair data) + { + Init(); + double a = 3.1415926535; + double b = -3.1415926535; + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle resA = deserializer.DeserializeJSTaggedValue(); + JSHandle resB = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!resA.IsEmpty() && !resB.IsEmpty()) << "[Empty] Deserialize double fail"; + EXPECT_TRUE(resA->IsDouble() && resB->IsDouble()) << "[NotInt] Deserialize double fail"; + EXPECT_TRUE(resA->GetDouble() == a) << "Not Same Value"; + EXPECT_TRUE(resB->GetDouble() == b) << "Not Same Value"; + Destroy(); + } + + void JSDateTest(std::pair data) + { + Init(); + double tm = 28 * 60 * 60 * 1000; // 28 * 60 * 60 * 1000 : test case + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSDate fail"; + EXPECT_TRUE(res->IsDate()) << "[NotJSDate] Deserialize JSDate fail"; + JSHandle resDate = JSHandle(res); + EXPECT_TRUE(resDate->GetTimeValue() == JSTaggedValue(tm)) << "Not Same Time Value"; + Destroy(); + } + + void JSMapTest(std::pair data, const JSHandle &originMap) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSMap fail"; + EXPECT_TRUE(res->IsJSMap()) << "[NotJSMap] Deserialize JSMap fail"; + JSHandle resMap = JSHandle::Cast(res); + EXPECT_TRUE(originMap->GetSize() == resMap->GetSize()) << "the map size Not equal"; + uint32_t resSize = resMap->GetSize(); + for (uint32_t i = 0; i < resSize; i++) { + JSHandle resKey(thread, resMap->GetKey(i)); + JSHandle resValue(thread, resMap->GetValue(i)); + JSHandle key(thread, originMap->GetKey(i)); + JSHandle value(thread, originMap->GetValue(i)); + + JSHandle resKeyStr = JSHandle::Cast(resKey); + JSHandle keyStr = JSHandle::Cast(key); + EXPECT_TRUE(EcmaString::StringsAreEqual(*resKeyStr, *keyStr)) << "Not same map key"; + EXPECT_TRUE(JSTaggedValue::ToInt32(thread, resValue) == JSTaggedValue::ToInt32(thread, value)) + << "Not same map value"; + } + Destroy(); + } + + void JSArrayBufferTest(std::pair data, + const JSHandle &originArrayBuffer, int32_t byteLength, const char *msg) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSArrayBuffer fail"; + EXPECT_TRUE(res->IsArrayBuffer()) << "[NotJSArrayBuffer] Deserialize JSArrayBuffer fail"; + JSHandle resJSArrayBuffer = JSHandle::Cast(res); + JSTaggedValue resTaggedLength = resJSArrayBuffer->GetArrayBufferByteLength(); + int32_t resByteLength = static_cast(resTaggedLength.GetInt()); + EXPECT_TRUE(resByteLength == byteLength) << "Not Same ByteLength"; // 10 : test case + + JSHandle bufferData(thread, originArrayBuffer->GetArrayBufferData()); + auto np = JSHandle::Cast(bufferData); + void *buffer = np->GetExternalPointer(); + JSHandle resBufferData(thread, resJSArrayBuffer->GetArrayBufferData()); + JSHandle resNp = JSHandle::Cast(resBufferData); + void *resBuffer = resNp->GetExternalPointer(); + + for (int32_t i = 0; i < resByteLength; i++) { + EXPECT_TRUE(static_cast(resBuffer)[i] == static_cast(buffer)[i]) << "Not Same Buffer"; + } + + if (msg != nullptr) { + if (memcpy_s(resBuffer, byteLength, msg, byteLength) != EOK) { + EXPECT_TRUE(false) << " memcpy error"; + } + } + Destroy(); + } + + void JSRegexpTest(std::pair data) + { + Init(); + JSHandle pattern = thread->GetEcmaVM()->GetFactory()->NewFromString("key2"); + JSHandle flags = thread->GetEcmaVM()->GetFactory()->NewFromString("i"); + char buffer[] = "1234567"; // use char buffer to simulate byteCodeBuffer + uint32_t bufferSize = 7; + + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSRegExp fail"; + EXPECT_TRUE(res->IsJSRegExp()) << "[NotJSRegexp] Deserialize JSRegExp fail"; + JSHandle resJSRegexp(res); + + uint32_t resBufferSize = static_cast(resJSRegexp->GetLength().GetInt()); + EXPECT_TRUE(resBufferSize == bufferSize) << "Not Same Length"; + JSHandle originalSource(thread, resJSRegexp->GetOriginalSource()); + EXPECT_TRUE(originalSource->IsString()); + JSHandle originalFlags(thread, resJSRegexp->GetOriginalFlags()); + EXPECT_TRUE(originalFlags->IsString()); + EXPECT_TRUE(EcmaString::StringsAreEqual(*JSHandle(originalSource), *pattern)); + EXPECT_TRUE(EcmaString::StringsAreEqual(*JSHandle(originalFlags), *flags)); + + JSHandle resBufferData(thread, resJSRegexp->GetByteCodeBuffer()); + JSHandle resNp = JSHandle::Cast(resBufferData); + void *resBuffer = resNp->GetExternalPointer(); + + for (uint32_t i = 0; i < resBufferSize; i++) { + EXPECT_TRUE(static_cast(resBuffer)[i] == buffer[i]) << "Not Same ByteCode"; + } + Destroy(); + } + + void TypedArrayTest(std::pair data, const JSHandle &originTypedArray) + { + Init(); + JSHandle originTypedArrayName(thread, originTypedArray->GetTypedArrayName()); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize TypedArray fail"; + EXPECT_TRUE(res->IsJSInt8Array()) << "[NotJSInt8Array] Deserialize TypedArray fail"; + JSHandle resJSInt8Array = JSHandle::Cast(res); + + JSHandle typedArrayName(thread, resJSInt8Array->GetTypedArrayName()); + JSTaggedValue byteLength = resJSInt8Array->GetByteLength(); + JSTaggedValue byteOffset = resJSInt8Array->GetByteOffset(); + JSTaggedValue arrayLength = resJSInt8Array->GetArrayLength(); + JSHandle viewedArrayBuffer(thread, resJSInt8Array->GetViewedArrayBuffer()); + + EXPECT_TRUE(typedArrayName->IsString()); + EXPECT_TRUE(EcmaString::StringsAreEqual(*JSHandle(typedArrayName), + *JSHandle(originTypedArrayName))); + EXPECT_TRUE(byteLength.GetInt() == originTypedArray->GetByteLength().GetInt()) << "Not Same ByteLength"; + EXPECT_TRUE(byteOffset.GetInt() == originTypedArray->GetByteOffset().GetInt()) << "Not Same ByteOffset"; + EXPECT_TRUE(arrayLength.GetInt() == originTypedArray->GetArrayLength().GetInt()) << "Not Same ArrayLength"; + + // check arrayBuffer + JSHandle resJSArrayBuffer(viewedArrayBuffer); + JSHandle originArrayBuffer(thread, originTypedArray->GetViewedArrayBuffer()); + JSTaggedValue resTaggedLength = resJSArrayBuffer->GetArrayBufferByteLength(); + JSTaggedValue originTaggedLength = originArrayBuffer->GetArrayBufferByteLength(); + EXPECT_TRUE(resTaggedLength.GetInt() == originTaggedLength.GetInt()) << "Not same viewedBuffer length"; + JSHandle bufferData(thread, originArrayBuffer->GetArrayBufferData()); + JSHandle np = JSHandle::Cast(bufferData); + void *buffer = np->GetExternalPointer(); + JSHandle resBufferData(thread, resJSArrayBuffer->GetArrayBufferData()); + JSHandle resNp = JSHandle::Cast(resBufferData); + void *resBuffer = resNp->GetExternalPointer(); + for (int32_t i = 0; i < resTaggedLength.GetInt(); i++) { + EXPECT_TRUE(static_cast(resBuffer)[i] == static_cast(buffer)[i]) << "Not same viewedBuffer"; + } + Destroy(); + } + +private: + EcmaVM *ecmaVm = nullptr; + EcmaHandleScope *scope = nullptr; + JSThread *thread = nullptr; +}; + +class JSSerializerTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + ecmaVm = EcmaVM::Cast(instance); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + JSThread *thread {nullptr}; + PandaVM *instance {nullptr}; + EcmaVM *ecmaVm {nullptr}; + EcmaHandleScope *scope {nullptr}; +}; + +HWTEST_F_L0(JSSerializerTest, SerializeJSSpecialValue) +{ + JSSerializer *serializer = new JSSerializer(thread); + JSHandle jsTrue(thread, JSTaggedValue::True()); + JSHandle jsFalse(thread, JSTaggedValue::False()); + JSHandle jsUndefined(thread, JSTaggedValue::Undefined()); + JSHandle jsNull(thread, JSTaggedValue::Null()); + JSHandle jsHole(thread, JSTaggedValue::Hole()); + serializer->SerializeJSTaggedValue(jsTrue); + serializer->SerializeJSTaggedValue(jsFalse); + serializer->SerializeJSTaggedValue(jsUndefined); + serializer->SerializeJSTaggedValue(jsNull); + serializer->SerializeJSTaggedValue(jsHole); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSSpecialValueTest, jsDeserializerTest, data); + t1.join(); +}; + +HWTEST_F_L0(JSSerializerTest, SerializeJSPlainObject) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle obj1 = factory->NewEmptyJSObject(); + JSHandle obj2 = factory->NewEmptyJSObject(); + + JSHandle key1(factory->NewFromString("2")); + JSHandle key2(factory->NewFromString("3")); + JSHandle key3(factory->NewFromString("x")); + JSHandle key4(factory->NewFromString("y")); + JSHandle key5(factory->NewFromString("a")); + JSHandle key6(factory->NewFromString("b")); + JSHandle key7(factory->NewFromString("5")); + JSHandle key8(factory->NewFromString("6")); + JSHandle value1(thread, JSTaggedValue(1)); + JSHandle value2(thread, JSTaggedValue(2)); + JSHandle value3(thread, JSTaggedValue(3)); + JSHandle value4(thread, JSTaggedValue(4)); + JSHandle value5(thread, JSTaggedValue(5)); + JSHandle value6(thread, JSTaggedValue(6)); + JSHandle value7(thread, JSTaggedValue(7)); + JSHandle value8(thread, JSTaggedValue(8)); + + JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); + JSObject::SetProperty(thread, JSHandle(obj1), key2, value2); + JSObject::SetProperty(thread, JSHandle(obj1), key3, value3); + JSObject::SetProperty(thread, JSHandle(obj1), key4, value4); + JSObject::SetProperty(thread, JSHandle(obj2), key5, value5); + JSObject::SetProperty(thread, JSHandle(obj2), key6, value6); + JSObject::SetProperty(thread, JSHandle(obj2), key7, value7); + JSObject::SetProperty(thread, JSHandle(obj2), key8, value8); + + JSSerializer *serializer = new JSSerializer(thread); + bool success1 = serializer->SerializeJSTaggedValue(JSHandle::Cast(obj1)); + bool success2 = serializer->SerializeJSTaggedValue(JSHandle::Cast(obj2)); + + EXPECT_TRUE(success1); + EXPECT_TRUE(success2); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSPlainObjectTest, jsDeserializerTest, data); + t1.join(); +}; + +HWTEST_F_L0(JSSerializerTest, TestSerializeDescription) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle obj = factory->NewEmptyJSObject(); + + JSHandle key1(factory->NewFromString("x")); + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromString("y")); + + PropertyDescriptor desc1(thread); + JSHandle value1(thread, JSTaggedValue(1)); + desc1.SetValue(value1); + desc1.SetWritable(true); + desc1.SetEnumerable(false); + desc1.SetConfigurable(true); + JSObject::DefineOwnProperty(thread, obj, key1, desc1); + + PropertyDescriptor desc2(thread); + JSHandle value2(thread, JSTaggedValue(2)); + desc2.SetValue(value2); + desc2.SetWritable(false); + desc2.SetEnumerable(true); + desc2.SetConfigurable(false); + JSObject::DefineOwnProperty(thread, obj, key2, desc2); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(obj)); + EXPECT_TRUE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::DescriptionTest, jsDeserializerTest, data); + t1.join(); +}; + +HWTEST_F_L0(JSSerializerTest, TestSerializeJSSet) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle constructor = env->GetBuiltinsSetFunction(); + JSHandle set = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + JSTaggedValue linkedSet = LinkedHashSet::Create(thread); + set->SetLinkedSet(thread, linkedSet); + // set property to set + JSHandle value1(thread, JSTaggedValue(7)); + JSHandle value2(thread, JSTaggedValue(9)); + JSHandle value3(factory->NewFromString("x")); + JSHandle value4(factory->NewFromString("y")); + + JSSet::Add(thread, set, value1); + JSSet::Add(thread, set, value2); + JSSet::Add(thread, set, value3); + JSSet::Add(thread, set, value4); + + // set property to object + JSHandle key1(factory->NewFromString("5")); + JSHandle key2(factory->NewFromString("6")); + + JSObject::SetProperty(thread, JSHandle(set), key1, value1); + JSObject::SetProperty(thread, JSHandle(set), key2, value2); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(set)); + EXPECT_TRUE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSSetTest, jsDeserializerTest, data); + t1.join(); +}; + +HWTEST_F_L0(JSSerializerTest, TestSerializeJSArray) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle array = factory->NewJSArray(); + + // set property to object + JSHandle key1(factory->NewFromString("abasd")); + JSHandle key2(factory->NewFromString("qweqwedasd")); + + JSHandle value1(thread, JSTaggedValue(7)); + JSHandle value2(thread, JSTaggedValue(9)); + + JSObject::SetProperty(thread, JSHandle(array), key1, value1); + JSObject::SetProperty(thread, JSHandle(array), key2, value2); + + // set value to array + array->SetArrayLength(thread, 20); + for (int i = 0; i < 20; i++) { + JSHandle data(thread, JSTaggedValue(i)); + JSArray::FastSetPropertyByValue(thread, JSHandle::Cast(array), i, data); + } + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(array)); + EXPECT_TRUE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSArrayTest, jsDeserializerTest, data); + t1.join(); +}; + +// Test the situation that Objects' properties stores values that reference with each other +HWTEST_F_L0(JSSerializerTest, TestObjectsPropertyReference) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle obj1 = factory->NewEmptyJSObject(); + JSHandle obj2 = factory->NewEmptyJSObject(); + [[maybe_unused]] JSHandle obj3 = factory->NewEmptyJSObject(); + + JSHandle key1(factory->NewFromString("abc")); + JSHandle key2(factory->NewFromString("def")); + JSHandle key3(factory->NewFromString("dgsdgf")); + JSHandle key4(factory->NewFromString("qwjhrf")); + + JSHandle value3(thread, JSTaggedValue(10)); + JSHandle value4(thread, JSTaggedValue(5)); + + // set property to obj1 + JSObject::SetProperty(thread, JSHandle::Cast(obj1), key1, JSHandle::Cast(obj2)); + JSObject::SetProperty(thread, JSHandle::Cast(obj1), key3, value3); + + // set property to obj2 + JSObject::SetProperty(thread, JSHandle::Cast(obj2), key2, JSHandle::Cast(obj1)); + JSObject::SetProperty(thread, JSHandle::Cast(obj2), key4, value4); + + JSSerializer *serializer = new JSSerializer(thread); + bool success1 = serializer->SerializeJSTaggedValue(JSHandle::Cast(obj1)); + bool success2 = serializer->SerializeJSTaggedValue(JSHandle::Cast(obj1)); + EXPECT_TRUE(success1) << "Serialize obj1 fail"; + EXPECT_TRUE(success2) << "Serialize obj2 fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::ObjectsPropertyReferenceTest, jsDeserializerTest, data); + t1.join(); +}; + +HWTEST_F_L0(JSSerializerTest, SerializeEcmaString) +{ + const char *rawStr = "this is a test ecmaString"; + JSHandle ecmaString = thread->GetEcmaVM()->GetFactory()->NewFromString(rawStr); + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle(ecmaString)); + EXPECT_TRUE(success) << "Serialize EcmaString fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::EcmaStringTest, jsDeserializerTest, data); + t1.join(); +}; + +HWTEST_F_L0(JSSerializerTest, SerializeInt32_t) +{ + JSSerializer *serializer = new JSSerializer(thread); + int32_t a = 64, min = -2147483648, b = -63; + JSTaggedValue aTag(a), minTag(min), bTag(b); + bool success1 = serializer->SerializeJSTaggedValue(JSHandle(thread, aTag)); + bool success2 = serializer->SerializeJSTaggedValue(JSHandle(thread, minTag)); + bool success3 = serializer->SerializeJSTaggedValue(JSHandle(thread, bTag)); + EXPECT_TRUE(success1 && success2 && success3) << "Serialize Int32 fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::Int32Test, jsDeserializerTest, data); + t1.join(); +}; + +HWTEST_F_L0(JSSerializerTest, SerializeDouble) +{ + JSSerializer *serializer = new JSSerializer(thread); + double a = 3.1415926535, b = -3.1415926535; + JSTaggedValue aTag(a), bTag(b); + bool success1 = serializer->SerializeJSTaggedValue(JSHandle(thread, aTag)); + bool success2 = serializer->SerializeJSTaggedValue(JSHandle(thread, bTag)); + EXPECT_TRUE(success1 && success2) << "Serialize Double fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::DoubleTest, jsDeserializerTest, data); + t1.join(); +}; + +JSDate *JSDateCreate(EcmaVM *ecmaVM) +{ + ObjectFactory *factory = ecmaVM->GetFactory(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle dateFunction = globalEnv->GetDateFunction(); + JSHandle dateObject = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(dateFunction), dateFunction)); + return *dateObject; +} + +HWTEST_F_L0(JSSerializerTest, SerializeDate) +{ + double tm = 28 * 60 * 60 * 1000; + JSHandle jsDate(thread, JSDateCreate(ecmaVm)); + jsDate->SetTimeValue(thread, JSTaggedValue(tm)); + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(jsDate)); + EXPECT_TRUE(success) << "Serialize JSDate fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSDateTest, jsDeserializerTest, data); + t1.join(); +}; + +JSMap *CreateMap(JSThread *thread) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle constructor = env->GetBuiltinsMapFunction(); + JSHandle map = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + JSTaggedValue linkedMap = LinkedHashMap::Create(thread); + map->SetLinkedMap(thread, linkedMap); + return *map; +} + +HWTEST_F_L0(JSSerializerTest, SerializeJSMap) +{ + JSHandle map(thread, CreateMap(thread)); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle key1(factory->NewFromString("3")); + JSHandle value1(thread, JSTaggedValue(12345)); + JSMap::Set(thread, map, key1, value1); + JSHandle key2(factory->NewFromString("key1")); + JSHandle value2(thread, JSTaggedValue(34567)); + JSMap::Set(thread, map, key2, value2); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(map)); + EXPECT_TRUE(success) << "Serialize JSMap fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSMapTest, jsDeserializerTest, data, map); + t1.join(); +}; + +JSArrayBuffer *CreateJSArrayBuffer(JSThread *thread) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle target = env->GetArrayBufferFunction(); + JSHandle jsArrayBuffer = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + return *jsArrayBuffer; +} + +HWTEST_F_L0(JSSerializerTest, SerializeJSArrayBuffer) +{ + JSHandle jsArrayBuffer(thread, CreateJSArrayBuffer(thread)); + int32_t byteLength = 10; + thread->GetEcmaVM()->GetFactory()->NewJSArrayBufferData(jsArrayBuffer, byteLength); + jsArrayBuffer->SetArrayBufferByteLength(thread, JSTaggedValue(static_cast(byteLength))); + JSHandle obj = JSHandle(jsArrayBuffer); + BuiltinsArrayBuffer::SetValueInBuffer(obj.GetTaggedValue(), 1, DataViewType::UINT8, JSTaggedNumber(7), true); + BuiltinsArrayBuffer::SetValueInBuffer(obj.GetTaggedValue(), 3, DataViewType::UINT8, JSTaggedNumber(17), true); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(jsArrayBuffer)); + EXPECT_TRUE(success) << "Serialize JSArrayBuffer fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSArrayBufferTest, jsDeserializerTest, data, jsArrayBuffer, 10, nullptr); + t1.join(); +}; + +HWTEST_F_L0(JSSerializerTest, SerializeJSArrayBufferShared) +{ + std::string msg = "hello world"; + int msgBufferLen = msg.length() + 1; + char* msgBuffer = new char[msgBufferLen] { 0 }; + if (memcpy_s(msgBuffer, msgBufferLen, msg.c_str(), msgBufferLen) != EOK) { + delete[] msgBuffer; + EXPECT_TRUE(false) << " memcpy error"; + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle jsArrayBuffer = factory->NewJSArrayBuffer(msgBuffer, msgBufferLen, nullptr, nullptr); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(jsArrayBuffer)); + EXPECT_TRUE(success) << "Serialize JSArrayBuffer fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSArrayBufferTest, jsDeserializerTest, data, jsArrayBuffer, 12, nullptr); + t1.join(); +}; + +HWTEST_F_L0(JSSerializerTest, SerializeJSArrayBufferShared2) +{ + std::string msg = "hello world"; + int msgBufferLen = msg.length() + 1; + char* msgBuffer = new char[msgBufferLen] { 0 }; + if (memcpy_s(msgBuffer, msgBufferLen, msg.c_str(), msgBufferLen) != EOK) { + delete[] msgBuffer; + EXPECT_TRUE(false) << " memcpy error"; + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle jsArrayBuffer = factory->NewJSArrayBuffer(msgBuffer, msgBufferLen, nullptr, nullptr, true); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(jsArrayBuffer)); + EXPECT_TRUE(success) << "Serialize JSArrayBuffer fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::string changeStr = "world hello"; + std::thread t1(&JSDeserializerTest::JSArrayBufferTest, + jsDeserializerTest, data, jsArrayBuffer, 12, changeStr.c_str()); + t1.join(); + EXPECT_TRUE(strcmp(msgBuffer, "world hello") == 0) << "Serialize JSArrayBuffer fail"; +}; + +HWTEST_F_L0(JSSerializerTest, SerializeJSRegExp) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle target = env->GetRegExpFunction(); + JSHandle jsRegexp = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + JSHandle pattern = thread->GetEcmaVM()->GetFactory()->NewFromString("key2"); + JSHandle flags = thread->GetEcmaVM()->GetFactory()->NewFromString("i"); + char buffer[] = "1234567"; // use char to simulate bytecode + uint32_t bufferSize = 7; + factory->NewJSRegExpByteCodeData(jsRegexp, static_cast(buffer), bufferSize); + jsRegexp->SetOriginalSource(thread, JSHandle(pattern)); + jsRegexp->SetOriginalFlags(thread, JSHandle(flags)); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(jsRegexp)); + EXPECT_TRUE(success) << "Serialize JSRegExp fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSRegexpTest, jsDeserializerTest, data); + t1.join(); +}; + +JSArrayBuffer *CreateTestJSArrayBuffer(JSThread *thread) +{ + JSHandle jsArrayBuffer(thread, CreateJSArrayBuffer(thread)); + int32_t byteLength = 10; + thread->GetEcmaVM()->GetFactory()->NewJSArrayBufferData(jsArrayBuffer, byteLength); + jsArrayBuffer->SetArrayBufferByteLength(thread, JSTaggedValue(static_cast(byteLength))); + JSHandle obj = JSHandle(jsArrayBuffer); + // 7 : test case + BuiltinsArrayBuffer::SetValueInBuffer(obj.GetTaggedValue(), 1, DataViewType::UINT8, JSTaggedNumber(7), true); + // 3, 17 : test case + BuiltinsArrayBuffer::SetValueInBuffer(obj.GetTaggedValue(), 3, DataViewType::UINT8, JSTaggedNumber(17), true); + return *jsArrayBuffer; +} + +HWTEST_F_L0(JSSerializerTest, SerializeJSTypedArray) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle target = env->GetInt8ArrayFunction(); + JSHandle int8Array = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + JSHandle viewedArrayBuffer(thread, CreateTestJSArrayBuffer(thread)); + int8Array->SetViewedArrayBuffer(thread, viewedArrayBuffer); + int byteLength = 10; + int byteOffset = 0; + int arrayLength = (byteLength - byteOffset) / (sizeof(int8_t)); + int8Array->SetByteLength(thread, JSTaggedValue(byteLength)); + int8Array->SetByteOffset(thread, JSTaggedValue(byteOffset)); + int8Array->SetTypedArrayName(thread, thread->GlobalConstants()->GetInt8ArrayString()); + int8Array->SetArrayLength(thread, JSTaggedValue(arrayLength)); + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(int8Array)); + EXPECT_TRUE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::TypedArrayTest, jsDeserializerTest, data, int8Array); + t1.join(); +}; + +// not support function +HWTEST_F_L0(JSSerializerTest, SerializeObjectWithFunction) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle function = env->GetRegExpFunction(); + EXPECT_TRUE(function->IsJSFunction()); + JSHandle key(factory->NewFromString("2")); + JSHandle obj = factory->NewEmptyJSObject(); + JSObject::SetProperty(thread, JSHandle(obj), key, function); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle(obj)); + EXPECT_FALSE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle ret = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(ret.IsEmpty()); +}; + +// not support symbol +HWTEST_F_L0(JSSerializerTest, SerializeSymbolWithProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle jsSymbol = factory->NewJSSymbol(); + JSHandle key1(factory->NewFromString("2")); + JSHandle key2(factory->NewFromString("x")); + JSHandle value1(thread, JSTaggedValue(1)); + JSHandle value2(thread, JSTaggedValue(8)); + JSObject::SetProperty(thread, JSHandle(jsSymbol), key1, value1); + JSObject::SetProperty(thread, JSHandle(jsSymbol), key2, value2); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle(jsSymbol)); + EXPECT_FALSE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle ret = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(ret.IsEmpty()); +}; +} // namespace panda::test diff --git a/ecmascript/tests/js_set_test.cpp b/ecmascript/tests/js_set_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1c900407b6f1e7d1de043fed7be91f17e56a340c --- /dev/null +++ b/ecmascript/tests/js_set_test.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/linked_hash_table-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_hash_table-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +namespace panda::test { +class JSSetTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + +protected: + JSSet *CreateSet() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle constructor = env->GetBuiltinsSetFunction(); + JSHandle set = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + LinkedHashSet *hashSet = LinkedHashSet::Cast(LinkedHashSet::Create(thread).GetTaggedObject()); + set->SetLinkedSet(thread, JSTaggedValue(hashSet)); + return JSSet::Cast(set.GetTaggedValue().GetTaggedObject()); + } +}; + +HWTEST_F_L0(JSSetTest, SetCreate) +{ + JSSet *set = CreateSet(); + EXPECT_TRUE(set != nullptr); +} + +HWTEST_F_L0(JSSetTest, AddAndHas) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // create jsSet + JSHandle set(thread, CreateSet()); + + JSHandle key(factory->NewFromString("key")); + JSSet::Add(thread, set, key); + EXPECT_TRUE(set->Has(key.GetTaggedValue())); +} + +HWTEST_F_L0(JSSetTest, DeleteAndGet) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // create jsSet + JSHandle set(thread, CreateSet()); + + // add 40 keys + char keyArray[] = "key0"; + for (int i = 0; i < 40; i++) { + keyArray[3] = '1' + i; + JSHandle key(factory->NewFromString(keyArray)); + JSSet::Add(thread, set, key); + EXPECT_TRUE(set->Has(key.GetTaggedValue())); + } + EXPECT_EQ(set->GetSize(), 40); + // whether jsSet has delete key + keyArray[3] = '1' + 8; + JSHandle deleteKey(factory->NewFromString(keyArray)); + JSSet::Delete(thread, set, deleteKey); + EXPECT_FALSE(set->Has(deleteKey.GetTaggedValue())); + EXPECT_EQ(set->GetSize(), 39); +} + +HWTEST_F_L0(JSSetTest, Iterator) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle set(thread, CreateSet()); + for (int i = 0; i < 5; i++) { + JSHandle key(thread, JSTaggedValue(i)); + JSSet::Add(thread, set, key); + } + + JSHandle keyIter(factory->NewJSSetIterator(set, IterationKind::KEY)); + JSHandle valueIter(factory->NewJSSetIterator(set, IterationKind::VALUE)); + + JSHandle keyResult0 = JSIterator::IteratorStep(thread, keyIter); + JSHandle valueResult0 = JSIterator::IteratorStep(thread, valueIter); + + EXPECT_EQ(0, JSIterator::IteratorValue(thread, keyResult0)->GetInt()); + EXPECT_EQ(0, JSIterator::IteratorValue(thread, valueResult0)->GetInt()); + + JSHandle keyResult1 = JSIterator::IteratorStep(thread, keyIter); + EXPECT_EQ(1, JSIterator::IteratorValue(thread, keyResult1)->GetInt()); + + for (int i = 0; i < 3; i++) { + JSHandle key(thread, JSTaggedValue(i)); + JSSet::Delete(thread, set, key); + } + + JSHandle keyResult2 = JSIterator::IteratorStep(thread, keyIter); + EXPECT_EQ(3, JSIterator::IteratorValue(thread, keyResult2)->GetInt()); + JSHandle keyResult3 = JSIterator::IteratorStep(thread, keyIter); + EXPECT_EQ(4, JSIterator::IteratorValue(thread, keyResult3)->GetInt()); + JSHandle key(thread, JSTaggedValue(5)); + JSSet::Add(thread, set, key); + JSHandle keyResult4 = JSIterator::IteratorStep(thread, keyIter); + EXPECT_EQ(5, JSIterator::IteratorValue(thread, keyResult4)->GetInt()); + JSHandle keyResult5 = JSIterator::IteratorStep(thread, keyIter); + EXPECT_EQ(JSTaggedValue::False(), keyResult5.GetTaggedValue()); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_symbol_test.cpp b/ecmascript/tests/js_symbol_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..80e2625b723685e6b77ee0d4863148e67354ff1f --- /dev/null +++ b/ecmascript/tests/js_symbol_test.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using JSSymbol = panda::ecmascript::JSSymbol; +using JSTaggedValue = panda::ecmascript::JSTaggedValue; +using LexicalEnv = panda::ecmascript::LexicalEnv; +using JSHClass = panda::ecmascript::JSHClass; +using ObjectFactory = panda::ecmascript::ObjectFactory; + +template +using JSHandle = panda::ecmascript::JSHandle; + +namespace panda::test { +class JSSymbolTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; +} // namespace panda::test diff --git a/ecmascript/tests/js_tagged_queue_test.cpp b/ecmascript/tests/js_tagged_queue_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ac00b12f67e9fa990d748d8e81b79549c0eabbd5 --- /dev/null +++ b/ecmascript/tests/js_tagged_queue_test.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" + +#include "ecmascript/global_env.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/snapshot/mem/snapshot.h" +#include "ecmascript/tagged_queue-inl.h" +#include "ecmascript/tagged_queue.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JSTaggedQueueTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(JSTaggedQueueTest, Create) +{ + JSHandle queue = thread->GetEcmaVM()->GetFactory()->NewTaggedQueue(0); + EXPECT_TRUE(*queue != nullptr); + EXPECT_TRUE(queue->Empty()); + EXPECT_TRUE(queue->Size() == 0); + EXPECT_TRUE(queue->Front() == JSTaggedValue::Hole()); + EXPECT_TRUE(queue->Back() == JSTaggedValue::Hole()); +} + +HWTEST_F_L0(JSTaggedQueueTest, PopAndPush) +{ + JSHandle queue = thread->GetEcmaVM()->GetFactory()->NewTaggedQueue(0); + EXPECT_TRUE(queue->Empty()); + + JSHandle queue2(thread, + TaggedQueue::Push(thread, queue, JSHandle(thread, JSTaggedValue(0)))); + EXPECT_FALSE(queue2->Empty()); + EXPECT_EQ(queue2->Size(), 1); + EXPECT_EQ(queue2->Front(), JSTaggedValue(0)); + EXPECT_EQ(queue2->Back(), JSTaggedValue(0)); + + JSHandle queue3(thread, + TaggedQueue::Push(thread, queue2, JSHandle(thread, JSTaggedValue(1)))); + EXPECT_EQ(queue3->Size(), 2); + EXPECT_EQ(queue3->Front(), JSTaggedValue(0)); + EXPECT_EQ(queue3->Back(), JSTaggedValue(1)); + EXPECT_NE(queue3.GetTaggedValue(), queue2.GetTaggedValue()); + + JSHandle queue4(thread, + TaggedQueue::Push(thread, queue3, JSHandle(thread, JSTaggedValue(2)))); + EXPECT_EQ(queue4->Size(), 3); + EXPECT_EQ(queue4->Front(), JSTaggedValue(0)); + EXPECT_EQ(queue4->Back(), JSTaggedValue(2)); + EXPECT_NE(queue4.GetTaggedValue(), queue3.GetTaggedValue()); + + JSHandle queue5(thread, + TaggedQueue::Push(thread, queue4, JSHandle(thread, JSTaggedValue(3)))); + EXPECT_EQ(queue5->Size(), 4); + EXPECT_EQ(queue5->Front(), JSTaggedValue(0)); + EXPECT_EQ(queue5->Back(), JSTaggedValue(3)); + EXPECT_NE(queue5.GetTaggedValue(), queue4.GetTaggedValue()); + + EXPECT_EQ(queue5->Pop(thread), JSTaggedValue(0)); + EXPECT_EQ(queue5->Size(), 3); + EXPECT_EQ(queue5->Front(), JSTaggedValue(1)); + + EXPECT_EQ(queue5->Pop(thread), JSTaggedValue(1)); + EXPECT_EQ(queue5->Size(), 2); + EXPECT_EQ(queue5->Front(), JSTaggedValue(2)); + + EXPECT_EQ(queue5->Pop(thread), JSTaggedValue(2)); + EXPECT_EQ(queue5->Size(), 1); + EXPECT_EQ(queue5->Front(), JSTaggedValue(3)); + + EXPECT_EQ(queue5->Pop(thread), JSTaggedValue(3)); + EXPECT_EQ(queue5->Size(), 0); + EXPECT_EQ(queue5->Front(), JSTaggedValue::Hole()); + EXPECT_TRUE(queue5->Empty()); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_verification_test.cpp b/ecmascript/tests/js_verification_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..04336aed791f8b52d49081fc5be0d83a57357fb0 --- /dev/null +++ b/ecmascript/tests/js_verification_test.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_thread.h" + +#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/space.h" +#include "ecmascript/mem/verification.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; + +namespace panda::test { +class JSVerificationTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(JSVerificationTest, IsHeapAddress) +{ + auto ecmaVm = thread->GetEcmaVM(); + auto heap = const_cast(ecmaVm->GetHeap()); + auto objectFactory = ecmaVm->GetFactory(); + auto verifier = Verification(heap); + EXPECT_FALSE(verifier.IsHeapAddress(reinterpret_cast(1))); + EXPECT_FALSE(verifier.IsHeapAddress(reinterpret_cast(2))); + EXPECT_FALSE(verifier.IsHeapAddress(nullptr)); + EXPECT_FALSE(verifier.IsHeapAddress(&verifier)); + + auto funcVerify = [](TaggedObject *object, Verification &v, const Heap *heap) { + EXPECT_TRUE(v.IsHeapAddress(reinterpret_cast(object))); + EXPECT_TRUE(heap->ContainObject(object)); + EXPECT_TRUE(heap->IsLive(object)); + }; + + // new space object + JSHandle string = objectFactory->NewFromString("123"); + funcVerify(*string, verifier, heap); + + // old space object + auto oldArray = objectFactory->NewTaggedArray(2, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); + funcVerify(*oldArray, verifier, heap); + + // no movable object + auto nonMovableArray = objectFactory->NewTaggedArray(2, JSTaggedValue::Undefined(), MemSpaceType::NON_MOVABLE); + funcVerify(*nonMovableArray, verifier, heap); +} + +HWTEST_F_L0(JSVerificationTest, VerifyHeapObjects) +{ + auto ecmaVm = thread->GetEcmaVM(); + auto heap = const_cast(ecmaVm->GetHeap()); + auto objectFactory = ecmaVm->GetFactory(); + EXPECT_TRUE(heap->VerifyHeapObjects() == 0); // failcount is 0 + + JSTaggedValue oldArray; + auto verifier = Verification(heap); + { + EcmaHandleScope handleScope(thread); + auto newArray = objectFactory->NewTaggedArray(1, JSTaggedValue::Undefined(), MemSpaceType::SEMI_SPACE); + + oldArray = + (objectFactory->NewTaggedArray(1, JSTaggedValue::Undefined(), MemSpaceType::NON_MOVABLE)).GetTaggedValue(); + newArray->Set(thread, 0, oldArray); + } + heap->CollectGarbage(panda::ecmascript::TriggerGCType::OLD_GC); + EXPECT_TRUE(verifier.VerifyRoot() == 0); + size_t failCount = 0; + VerifyObjectVisitor objVerifier(heap, &failCount); + const_cast(heap->GetNewSpace())->IterateOverObjects(objVerifier); // newspace reference the old space +} +} // namespace panda::test diff --git a/ecmascript/tests/large_object_test.cpp b/ecmascript/tests/large_object_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..807fcb5f8bb78380a1c2ff24bb3558a225fca54a --- /dev/null +++ b/ecmascript/tests/large_object_test.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/mem/space.h" +#include "ecmascript/mem/verification.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class LargeObjectTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + thread->GetEcmaVM()->GetFactory()->SetTriggerGc(false); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + JSThread *thread {nullptr}; + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; +}; + +#if !defined(NDEBUG) +static JSObject *JSObjectTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + EcmaVM *ecmaVM = thread->GetEcmaVM(); + auto globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle jsFunc = globalEnv->GetObjectFunction(); + JSHandle newObj = + ecmaVM->GetFactory()->NewJSObjectByConstructor(JSHandle(jsFunc), jsFunc); + return *newObj; +} +#endif + +#if !defined(NDEBUG) +static TaggedArray *LargeArrayTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + // 20 * 10000 : test case + JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(20 * 10000); + return *array; +} +#endif + +HWTEST_F_L0(LargeObjectTest, LargeArrayKeep) +{ +#if !defined(NDEBUG) + TaggedArray *array = LargeArrayTestCreate(thread); + EXPECT_TRUE(array != nullptr); + JSHandle arrayHandle(thread, array); + JSHandle newObj(thread, JSObjectTestCreate(thread)); + arrayHandle->Set(thread, 0, newObj.GetTaggedValue()); + auto ecmaVm = thread->GetEcmaVM(); + EXPECT_EQ(*arrayHandle, reinterpret_cast(array)); + ecmaVm->CollectGarbage(TriggerGCType::SEMI_GC); // Trigger GC. + ecmaVm->CollectGarbage(TriggerGCType::LARGE_GC); // Trigger GC. + EXPECT_EQ(*newObj, array->Get(0).GetTaggedObject()); + EXPECT_EQ(*arrayHandle, reinterpret_cast(array)); +#endif +} + +HWTEST_F_L0(LargeObjectTest, MultipleArrays) +{ +#if !defined(NDEBUG) + auto ecmaVm = thread->GetEcmaVM(); + auto heap = ecmaVm->GetHeap(); + Region *firstPage = nullptr; + Region *secondPage = nullptr; + Region *thirdPage = nullptr; + + JSHandle array1(thread, LargeArrayTestCreate(thread)); + firstPage = Region::ObjectAddressToRange(*array1); + { + DISALLOW_GARBAGE_COLLECTION; + [[maybe_unused]] TaggedArray *array2 = LargeArrayTestCreate(thread); + secondPage = Region::ObjectAddressToRange(array2); + } + JSHandle array3(thread, LargeArrayTestCreate(thread)); + thirdPage = Region::ObjectAddressToRange(*array3); + + EXPECT_EQ(firstPage->GetNext(), secondPage); + EXPECT_EQ(secondPage->GetNext(), thirdPage); + + ecmaVm->CollectGarbage(TriggerGCType::LARGE_GC); // Trigger GC. + + EXPECT_EQ(firstPage->GetNext(), thirdPage); + + size_t failCount = 0; + VerifyObjectVisitor objVerifier(heap, &failCount); + heap->GetLargeObjectSpace()->IterateOverObjects(objVerifier); // newspace reference the old space + EXPECT_TRUE(failCount == 0); +#endif +} +} // namespace panda::test diff --git a/ecmascript/tests/lexical_env_test.cpp b/ecmascript/tests/lexical_env_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..175f5108b91dcfea408d0ea9b6797135cab74e59 --- /dev/null +++ b/ecmascript/tests/lexical_env_test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tests/test_helper.h" + +#include "ecmascript/lexical_env.h" +#include "ecmascript/object_factory.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class LexicalEnvTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(LexicalEnvTest, LexicalEnv_Create) +{ + JSHandle lexicalEnv = thread->GetEcmaVM()->GetFactory()->NewLexicalEnv(0); + EXPECT_TRUE(lexicalEnv.GetTaggedValue().IsObject()); +} +} // namespace panda::test diff --git a/ecmascript/tests/linked_hash_table_test.cpp b/ecmascript/tests/linked_hash_table_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..80fc2d589efb2f1773b92c6b6cc1c2e8c19928d3 --- /dev/null +++ b/ecmascript/tests/linked_hash_table_test.cpp @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/linked_hash_table-inl.h" +#include "ecmascript/linked_hash_table.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_hash_table-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +namespace panda::test { +class LinkedHashTableTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + JSHandle GetGlobalEnv() + { + EcmaVM *ecma = thread->GetEcmaVM(); + return ecma->GetGlobalEnv(); + } +}; + +HWTEST_F_L0(LinkedHashTableTest, MapCreate) +{ + int numOfElement = 64; + LinkedHashMap *dict = LinkedHashMap::Cast(LinkedHashMap::Create(thread, numOfElement).GetTaggedObject()); + EXPECT_TRUE(dict != nullptr); +} + +HWTEST_F_L0(LinkedHashTableTest, SetCreate) +{ + int numOfElement = 64; + LinkedHashSet *set = LinkedHashSet::Cast(LinkedHashSet::Create(thread, numOfElement).GetTaggedObject()); + EXPECT_TRUE(set != nullptr); +} + +HWTEST_F_L0(LinkedHashTableTest, addKeyAndValue) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // mock object needed in test + int numOfElement = 64; + LinkedHashMap *dict = LinkedHashMap::Cast(LinkedHashMap::Create(thread, numOfElement).GetTaggedObject()); + EXPECT_TRUE(dict != nullptr); + JSHandle dictHandle(thread, dict); + JSHandle objFun = GetGlobalEnv()->GetObjectFunction(); + + char keyArray[] = "hello"; + JSHandle stringKey1 = factory->NewFromString(keyArray); + JSHandle key1(stringKey1); + JSHandle value1(factory->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + + char key2Array[] = "hello2"; + JSHandle stringKey2 = factory->NewFromString(key2Array); + JSHandle key2(stringKey2); + JSHandle value2(factory->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + + // test set() + dict = LinkedHashMap::Cast(LinkedHashMap::Set(thread, dictHandle, key1, value1).GetTaggedObject()); + EXPECT_EQ(dict->NumberOfElements(), 1); + + // test find() + int entry1 = dict->FindElement(key1.GetTaggedValue()); + EXPECT_EQ(key1.GetTaggedValue(), dict->GetKey(entry1)); + EXPECT_EQ(value1.GetTaggedValue(), dict->GetValue(entry1)); + + dict = LinkedHashMap::Cast(LinkedHashMap::Set(thread, dictHandle, key2, value2).GetTaggedObject()); + EXPECT_EQ(dict->NumberOfElements(), 2); + // test remove() + dict = LinkedHashMap::Cast(LinkedHashMap::Delete(thread, dictHandle, key1).GetTaggedObject()); + EXPECT_EQ(-1, dict->FindElement(key1.GetTaggedValue())); + EXPECT_EQ(dict->NumberOfElements(), 1); + + JSHandle undefinedKey(thread, JSTaggedValue::Undefined()); + dict = LinkedHashMap::Cast(LinkedHashMap::Set(thread, dictHandle, undefinedKey, value1).GetTaggedObject()); + int entry2 = dict->FindElement(undefinedKey.GetTaggedValue()); + EXPECT_EQ(value1.GetTaggedValue(), dict->GetValue(entry2)); +} + +HWTEST_F_L0(LinkedHashTableTest, SetaddKeyAndValue) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // mock object needed in test + int numOfElement = 64; + LinkedHashSet *set = LinkedHashSet::Cast(LinkedHashSet::Create(thread, numOfElement).GetTaggedObject()); + EXPECT_TRUE(set != nullptr); + JSHandle setHandle(thread, set); + JSHandle objFun = GetGlobalEnv()->GetObjectFunction(); + + char keyArray[] = "hello"; + JSHandle stringKey1 = factory->NewFromString(keyArray); + JSHandle key1(stringKey1); + JSHandle value1(factory->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + + char key2Array[] = "hello2"; + JSHandle stringKey2 = factory->NewFromString(key2Array); + JSHandle key2(stringKey2); + JSHandle value2(factory->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + + // test set() + set = LinkedHashSet::Cast(LinkedHashSet::Add(thread, setHandle, key1).GetTaggedObject()); + EXPECT_EQ(set->NumberOfElements(), 1); + + // test has() + EXPECT_TRUE(set->Has(key1.GetTaggedValue())); + + set = LinkedHashSet::Cast(LinkedHashSet::Add(thread, setHandle, key2).GetTaggedObject()); + EXPECT_EQ(set->NumberOfElements(), 2); + // test remove() + set = LinkedHashSet::Cast(LinkedHashSet::Delete(thread, setHandle, key1).GetTaggedObject()); + EXPECT_EQ(-1, set->FindElement(key1.GetTaggedValue())); + EXPECT_EQ(set->NumberOfElements(), 1); + + JSHandle undefinedKey(thread, JSTaggedValue::Undefined()); + set = LinkedHashSet::Cast(LinkedHashSet::Add(thread, setHandle, undefinedKey).GetTaggedObject()); + EXPECT_TRUE(set->Has(undefinedKey.GetTaggedValue())); +} + +HWTEST_F_L0(LinkedHashTableTest, GrowCapacity) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int numOfElement = 8; + LinkedHashMap *dict = LinkedHashMap::Cast(LinkedHashMap::Create(thread, numOfElement).GetTaggedObject()); + EXPECT_TRUE(dict != nullptr); + JSHandle objFun(GetGlobalEnv()->GetObjectFunction()); + char keyArray[7] = "hello"; + for (int i = 0; i < 33; i++) { + JSHandle dictHandle(thread, dict); + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle key(factory->NewFromString(keyArray)); + JSHandle value(thread, JSTaggedValue(i)); + + // test insert() + dict = LinkedHashMap::Cast(LinkedHashMap::Set(thread, dictHandle, key, value).GetTaggedObject()); + EXPECT_EQ(i, dict->FindElement(key.GetTaggedValue())); + } + + JSHandle dictHandle(thread, dict); + // test order + for (int i = 0; i < 33; i++) { + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle stringKey = factory->NewFromString(keyArray); + // test insert() + EXPECT_EQ(i, dictHandle->FindElement(stringKey.GetTaggedValue())); + } + EXPECT_EQ(dictHandle->NumberOfElements(), 33); + EXPECT_EQ(dictHandle->Capacity(), 64); +} + +HWTEST_F_L0(LinkedHashTableTest, SetGrowCapacity) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int numOfElement = 8; + LinkedHashSet *set = LinkedHashSet::Cast(LinkedHashSet::Create(thread, numOfElement).GetTaggedObject()); + EXPECT_TRUE(set != nullptr); + JSHandle objFun(GetGlobalEnv()->GetObjectFunction()); + // create key and values + char keyArray[7] = "hello"; + for (int i = 0; i < 33; i++) { + JSHandle setHandle(thread, set); + + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle stringKey = factory->NewFromString(keyArray); + JSHandle key(stringKey); + + // test insert() + set = LinkedHashSet::Cast(LinkedHashSet::Add(thread, setHandle, key).GetTaggedObject()); + EXPECT_EQ(i, set->FindElement(key.GetTaggedValue())); + } + + JSHandle setHandle(thread, set); + // test order + for (int i = 0; i < 33; i++) { + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle stringKey = factory->NewFromString(keyArray); + // test insert() + EXPECT_EQ(i, setHandle->FindElement(stringKey.GetTaggedValue())); + } + EXPECT_EQ(setHandle->NumberOfElements(), 33); + EXPECT_EQ(setHandle->Capacity(), 64); +} + +HWTEST_F_L0(LinkedHashTableTest, ShrinkCapacity) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int numOfElement = 64; + LinkedHashMap *dict = LinkedHashMap::Cast(LinkedHashMap::Create(thread, numOfElement).GetTaggedObject()); + EXPECT_TRUE(dict != nullptr); + JSHandle objFun(GetGlobalEnv()->GetObjectFunction()); + char keyArray[7] = "hello"; + for (int i = 0; i < 10; i++) { + JSHandle dictHandle(thread, dict); + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle key(factory->NewFromString(keyArray)); + JSHandle value(thread, JSTaggedValue(i)); + + // test insert() + dict = LinkedHashMap::Cast(LinkedHashMap::Set(thread, dictHandle, key, value).GetTaggedObject()); + } + JSHandle dictHandle(thread, dict); + keyArray[5] = '1' + 9; + JSHandle key(factory->NewFromString(keyArray)); + dict = LinkedHashMap::Cast(LinkedHashMap::Delete(thread, dictHandle, key).GetTaggedObject()); + JSHandle handle(thread, dict); + // test order + for (int i = 0; i < 9; i++) { + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle stringKey = factory->NewFromString(keyArray); + // test insert() + EXPECT_EQ(i, handle->FindElement(stringKey.GetTaggedValue())); + } + EXPECT_EQ(handle->NumberOfElements(), 9); + EXPECT_EQ(handle->Capacity(), 16); +} + +HWTEST_F_L0(LinkedHashTableTest, SetShrinkCapacity) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int numOfElement = 64; + LinkedHashSet *set = LinkedHashSet::Cast(LinkedHashSet::Create(thread, numOfElement).GetTaggedObject()); + EXPECT_TRUE(set != nullptr); + JSHandle objFun(GetGlobalEnv()->GetObjectFunction()); + // create key and values + char keyArray[7] = "hello"; + for (int i = 0; i < 10; i++) { + JSHandle setHandle(thread, set); + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle key(factory->NewFromString(keyArray)); + + // test insert() + set = LinkedHashSet::Cast(LinkedHashSet::Add(thread, setHandle, key).GetTaggedObject()); + } + JSHandle setHandle(thread, set); + keyArray[5] = '1' + 9; + JSHandle keyHandle(factory->NewFromString(keyArray)); + set = LinkedHashSet::Cast(LinkedHashSet::Delete(thread, setHandle, keyHandle).GetTaggedObject()); + JSHandle handle(thread, set); + // test order + for (int i = 0; i < 9; i++) { + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle stringKey = factory->NewFromString(keyArray); + // test insert() + EXPECT_EQ(i, handle->FindElement(stringKey.GetTaggedValue())); + } + EXPECT_EQ(handle->NumberOfElements(), 9); + EXPECT_EQ(handle->Capacity(), 16); +} +} // namespace panda::test diff --git a/ecmascript/tests/name_dictionary_test.cpp b/ecmascript/tests/name_dictionary_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2336141f547b514dfa6650e8ae3b0f6649c12ef9 --- /dev/null +++ b/ecmascript/tests/name_dictionary_test.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_dictionary.h" +#include "ecmascript/tagged_hash_table-inl.h" +#include "ecmascript/tagged_hash_table.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +namespace panda::test { +class NameDictionaryTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +static JSHandle GetGlobalEnv(JSThread *thread) +{ + EcmaVM *ecma = thread->GetEcmaVM(); + return ecma->GetGlobalEnv(); +} + +HWTEST_F_L0(NameDictionaryTest, createDictionary) +{ + int numOfElement = 64; + NameDictionary *dict = NameDictionary::Create(thread, numOfElement); + EXPECT_TRUE(dict != nullptr); +} + +HWTEST_F_L0(NameDictionaryTest, addKeyAndValue) +{ + // mock object needed in test + int numOfElement = 64; + JSHandle dictJShandle(thread, NameDictionary::Create(thread, numOfElement)); + EXPECT_TRUE(*dictJShandle != nullptr); + JSHandle dictHandle(dictJShandle); + JSHandle objFun = GetGlobalEnv(thread)->GetObjectFunction(); + + // create key and values + JSHandle jsObject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFun), objFun); + EXPECT_TRUE(*jsObject != nullptr); + + char keyArray[] = "hello"; + JSHandle stringKey1 = thread->GetEcmaVM()->GetFactory()->NewFromString(keyArray); + JSHandle key1(stringKey1); + JSHandle taggedkey1(stringKey1); + JSHandle value1( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + PropertyAttributes metaData1; + + char key2Array[] = "hello2"; + JSHandle stringKey2 = thread->GetEcmaVM()->GetFactory()->NewFromString(key2Array); + JSHandle key2(stringKey2); + JSHandle taggedkey2(stringKey2); + JSHandle value2( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + PropertyAttributes metaData2; + + // test insert() + JSHandle dict(thread, NameDictionary::PutIfAbsent(thread, dictHandle, key1, value1, metaData1)); + EXPECT_EQ(dict->EntriesCount(), 1); + + // test find() and lookup() + int entry1 = dict->FindEntry(key1.GetTaggedValue()); + EXPECT_EQ(key1.GetTaggedValue(), JSTaggedValue(dict->GetKey(entry1).GetRawData())); + EXPECT_EQ(value1.GetTaggedValue(), JSTaggedValue(dict->GetValue(entry1).GetRawData())); + + JSHandle dict2(thread, dict->PutIfAbsent(thread, dictHandle, key2, value2, metaData2)); + EXPECT_EQ(dict2->EntriesCount(), 2); + // test remove() + dict->Remove(thread, dictHandle, entry1); + EXPECT_EQ(-1, dict->FindEntry(key1.GetTaggedValue())); + EXPECT_EQ(dict->EntriesCount(), 1); +} + +HWTEST_F_L0(NameDictionaryTest, GrowCapacity) +{ + int numOfElement = 8; + JSHandle dictHandle(thread, NameDictionary::Create(thread, numOfElement)); + EXPECT_TRUE(*dictHandle != nullptr); + JSHandle objFun = GetGlobalEnv(thread)->GetObjectFunction(); + // create key and values + JSHandle jsObject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFun), objFun); + EXPECT_TRUE(*jsObject != nullptr); + char keyArray[7] = "hello"; + NameDictionary *dict = *dictHandle; + for (int i = 0; i < 9; i++) { + JSHandle tempHandle(thread, dict); + keyArray[5] = '1' + i; + keyArray[6] = 0; + + JSHandle stringKey = thread->GetEcmaVM()->GetFactory()->NewFromString(keyArray); + ecmascript::JSHandle key(stringKey); + JSHandle keyHandle(key); + ecmascript::JSHandle value(thread, JSTaggedValue(i)); + JSHandle valueHandle(value); + PropertyAttributes metaData; + + // test insert() + dict = NameDictionary::PutIfAbsent(thread, tempHandle, keyHandle, valueHandle, metaData); + } + EXPECT_EQ(dict->EntriesCount(), 9); + EXPECT_EQ(dict->Size(), 16); +} + +HWTEST_F_L0(NameDictionaryTest, ShrinkCapacity) +{ + int numOfElement = 64; + JSMutableHandle dictHandle(thread, NameDictionary::Create(thread, numOfElement)); + EXPECT_TRUE(*dictHandle != nullptr); + JSHandle objFun = GetGlobalEnv(thread)->GetObjectFunction(); + // create key and values + JSHandle jsObject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFun), objFun); + EXPECT_TRUE(*jsObject != nullptr); + uint8_t keyArray[7] = "hello"; + + auto stringTable = thread->GetEcmaVM()->GetEcmaStringTable(); + for (int i = 0; i < 10; i++) { + keyArray[5] = '0' + i; + keyArray[6] = 0; + + JSHandle key(thread, stringTable->GetOrInternString(keyArray, utf::Mutf8Size(keyArray))); + JSHandle value(thread, JSTaggedValue(i)); + PropertyAttributes metaData; + + // test insert() + dictHandle.Update(JSTaggedValue(NameDictionary::PutIfAbsent(thread, dictHandle, key, value, metaData))); + } + + keyArray[5] = '2'; + keyArray[6] = 0; + JSHandle arrayHandle(thread, stringTable->GetOrInternString(keyArray, utf::Mutf8Size(keyArray))); + + int entry = dictHandle->FindEntry(arrayHandle.GetTaggedValue()); + EXPECT_NE(entry, -1); + + dictHandle.Update(JSTaggedValue(NameDictionary::Remove(thread, dictHandle, entry))); + EXPECT_EQ(dictHandle->EntriesCount(), 9); + EXPECT_EQ(dictHandle->Size(), 16); +} +} // namespace panda::test diff --git a/ecmascript/tests/native_pointer_test.cpp b/ecmascript/tests/native_pointer_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4cabc96694d4c53e20a415ee454c3e31b71c393c --- /dev/null +++ b/ecmascript/tests/native_pointer_test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +using FunctionPtr = void (*)(panda::ecmascript::JSHandle &); + +namespace panda::test { +class NativePointerTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(NativePointerTest, Print) +{ + // mock object needed in test + char array[] = "Hello World!"; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle str = factory->NewFromString(array); + EXPECT_TRUE(*str != nullptr); + + JSHandle jsFunction = factory->NewJSFunction(env); + EXPECT_TRUE(*jsFunction != nullptr); + + JSMethod *target = thread->GetEcmaVM()->GetMethodForNativeFunction(nullptr); + jsFunction->SetCallTarget(thread, target); + + // run cpp methed 'Print' + ASSERT_EQ(target, jsFunction->GetCallTarget()); +} +} // namespace panda::test diff --git a/ecmascript/tests/object_factory_test.cpp b/ecmascript/tests/object_factory_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8ccf0bc74bfffafb6c9c3b0b7a695b55645dc824 --- /dev/null +++ b/ecmascript/tests/object_factory_test.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_string-inl.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/lexical_env.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +namespace panda::test { +class ObjectFactoryTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSHandle GetGlobal(JSThread *thread) +{ + return thread->GetEcmaVM()->GetGlobalEnv(); +} + +#ifndef NDEBUG +HWTEST_F_L0(ObjectFactoryTest, NewJSObjectByConstructor) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle objFun = GetGlobal(thread)->GetObjectFunction(); + + // check mem alloc + JSHandle newObj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle newObjCls(thread, newObj->GetJSHClass()); + EXPECT_TRUE(*newObj != nullptr); + EXPECT_TRUE(*newObjCls != nullptr); + + // check feild + EXPECT_EQ(newObj->GetProperties(), GetGlobal(thread)->GetEmptyArray().GetTaggedValue()); + EXPECT_EQ(newObj->GetElements(), GetGlobal(thread)->GetEmptyArray().GetTaggedValue()); + EXPECT_TRUE(JSTaggedValue(*newObj).IsECMAObject()); + + // check jshclass + JSHClass *cls = *newObjCls; + EXPECT_TRUE(cls->GetObjectSize() == + JSObject::SIZE + JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS * JSTaggedValue::TaggedTypeSize()); + EXPECT_TRUE(cls->GetPrototype() == GetGlobal(thread)->GetObjectFunctionPrototype().GetTaggedValue()); + EXPECT_TRUE(cls->GetObjectType() == JSType::JS_OBJECT); + + // check gc handle update + auto *prototype = cls->GetPrototype().GetTaggedObject(); + [[maybe_unused]] JSHandle newObj2 = + factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); + EXPECT_TRUE(prototype != newObjCls->GetPrototype().GetTaggedObject()); +} +#endif + +HWTEST_F_L0(ObjectFactoryTest, NewJSFunction) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + // check mem alloc + JSHandle newFun = factory->NewJSFunction(env); + JSHandle newFunCls(thread, newFun->GetJSHClass()); + EXPECT_TRUE(*newFun != nullptr); + EXPECT_TRUE(*newFunCls != nullptr); + + // check feild + EXPECT_EQ(newFun->GetProperties(), GetGlobal(thread)->GetEmptyArray().GetTaggedValue()); + EXPECT_EQ(newFun->GetElements(), GetGlobal(thread)->GetEmptyArray().GetTaggedValue()); + EXPECT_EQ(newFun->GetProtoOrDynClass(), JSTaggedValue::Hole()); + EXPECT_EQ(newFun->GetHomeObject(), JSTaggedValue::Undefined()); + EXPECT_TRUE(JSTaggedValue(*newFun).IsJSFunction()); + + // check jshclass + JSHClass *cls = *newFunCls; + EXPECT_TRUE(cls->GetObjectSize() == + JSFunction::SIZE + JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS * JSTaggedValue::TaggedTypeSize()); + EXPECT_TRUE(cls->GetPrototype() == GetGlobal(thread)->GetFunctionPrototype().GetTaggedValue()); + EXPECT_TRUE(cls->GetObjectType() == JSType::JS_FUNCTION); + EXPECT_TRUE(cls->IsCallable()); + EXPECT_TRUE(cls->IsExtensible()); + EXPECT_TRUE(!cls->IsConstructor()); +} + +HWTEST_F_L0(ObjectFactoryTest, NewJSBoundFunction) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // test prepare + JSHandle funFun(GetGlobal(thread)->GetObjectFunction()); + JSHandle bound(thread, GetGlobal(thread)->GetObjectFunctionPrototype().GetTaggedValue()); + const JSHandle array(GetGlobal(thread)->GetEmptyArray()); + + // check mem alloc + JSHandle targetFunc(funFun); + JSHandle newBoundFun = factory->NewJSBoundFunction(targetFunc, bound, array); + JSHandle newBoundFunCls(thread, newBoundFun->GetJSHClass()); + EXPECT_TRUE(*newBoundFun != nullptr); + EXPECT_TRUE(*newBoundFunCls != nullptr); +} + +HWTEST_F_L0(ObjectFactoryTest, NewJSPrimitiveRef) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // test prepare + JSHandle numberFun(GetGlobal(thread)->GetNumberFunction()); + JSHandle primitive(thread, JSTaggedValue(1)); + + // check mem alloc + JSHandle newPrimitive = factory->NewJSPrimitiveRef(numberFun, primitive); + JSHandle newPrimitiveCls(thread, newPrimitive->GetJSHClass()); + EXPECT_TRUE(*newPrimitive != nullptr); + EXPECT_TRUE(*newPrimitiveCls != nullptr); + + EXPECT_TRUE(newPrimitive->GetValue() == JSTaggedValue(1)); +} + +HWTEST_F_L0(ObjectFactoryTest, NewLexicalEnv) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // check mem alloc + JSHandle newLexicalEnv = factory->NewLexicalEnv(0); + JSHandle newLexicalEnvCls(thread, newLexicalEnv->GetClass()); + EXPECT_TRUE(*newLexicalEnv != nullptr); + EXPECT_TRUE(*newLexicalEnvCls != nullptr); +} + +HWTEST_F_L0(ObjectFactoryTest, NewJSArray) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // check mem alloc + JSHandle newJSAarray = factory->NewJSArray(); + JSHandle newJSArrayCls(thread, newJSAarray->GetJSHClass()); + EXPECT_TRUE(*newJSAarray != nullptr); + EXPECT_TRUE(*newJSArrayCls != nullptr); +} +} // namespace panda::test diff --git a/ecmascript/tests/tagged_value_test.cpp b/ecmascript/tests/tagged_value_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4a012555ce9dd4a8c7e0c6b71cb11038f918403b --- /dev/null +++ b/ecmascript/tests/tagged_value_test.cpp @@ -0,0 +1,1210 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_hclass.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_primitive_ref.h" +#include "ecmascript/js_symbol.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/snapshot/mem/snapshot.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JSTaggedValueTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(JSTaggedValueTest, Double) +{ + double d = 1.1; + JSTaggedValue td(d); + EXPECT_EQ(true, td.IsDouble()); + EXPECT_EQ(false, td.IsInt()); + EXPECT_EQ(false, td.IsObject()); + ASSERT_DOUBLE_EQ(td.GetDouble(), d); + + double nan = std::nan(""); + JSTaggedValue tNan(nan); + EXPECT_EQ(true, tNan.IsDouble()); + EXPECT_EQ(false, tNan.IsInt()); + EXPECT_EQ(false, tNan.IsObject()); + EXPECT_EQ(ReinterpretDoubleToTaggedType(tNan.GetDouble()), ReinterpretDoubleToTaggedType(nan)); + + double pureNaN = ReinterpretTaggedTypeToDouble(JSTaggedValue::TAG_INT - JSTaggedValue::DOUBLE_ENCODE_OFFSET); + EXPECT_EQ(true, JSTaggedValue::IsImpureNaN(pureNaN)); +} + +HWTEST_F_L0(JSTaggedValueTest, Int) +{ + int i = 0x5c; + JSTaggedValue t(0x5c); + EXPECT_EQ(true, t.IsInt()); + EXPECT_EQ(false, t.IsObject()); + EXPECT_EQ(false, t.IsDouble()); + EXPECT_EQ(t.GetInt(), i); +} + +HWTEST_F_L0(JSTaggedValueTest, IsObject) +{ + TaggedObject *p = reinterpret_cast(0xffff0000UL); + JSTaggedValue t(p); + EXPECT_EQ(true, t.IsObject()); + EXPECT_EQ(false, t.IsInt()); + EXPECT_EQ(false, t.IsDouble()); + EXPECT_EQ(t.GetTaggedObject(), p); +} + +HWTEST_F_L0(JSTaggedValueTest, WeakRef) +{ + TaggedObject *p = reinterpret_cast(0xffff0000UL); + JSTaggedValue tInt(0x5c); + JSTaggedValue t(p); + + t.CreateWeakRef(); + EXPECT_EQ(false, tInt.IsWeak()); + EXPECT_EQ(true, t.IsObject()); + EXPECT_EQ(true, t.IsWeak()); + EXPECT_EQ(false, t.IsInt()); + EXPECT_EQ(false, t.IsDouble()); + EXPECT_EQ(t.GetTaggedWeakRef(), p); +} + +HWTEST_F_L0(JSTaggedValueTest, False) +{ + JSTaggedValue t = JSTaggedValue::False(); + EXPECT_EQ(t.IsFalse(), true); +} + +HWTEST_F_L0(JSTaggedValueTest, True) +{ + JSTaggedValue t = JSTaggedValue::True(); + EXPECT_EQ(t.IsTrue(), true); +} + +HWTEST_F_L0(JSTaggedValueTest, Undefined) +{ + JSTaggedValue t = JSTaggedValue::Undefined(); + EXPECT_EQ(t.IsUndefined(), true); +} +HWTEST_F_L0(JSTaggedValueTest, Null) +{ + JSTaggedValue t = JSTaggedValue::Null(); + EXPECT_EQ(t.IsNull(), true); +} + +HWTEST_F_L0(JSTaggedValueTest, Hole) +{ + JSTaggedValue t = JSTaggedValue::Hole(); + EXPECT_EQ(t.IsHole(), true); +} + +HWTEST_F_L0(JSTaggedValueTest, ToPrimitive) +{ + JSTaggedValue result; + + JSTaggedValue intV(100); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, intV)); + EXPECT_EQ(result.GetInt(), 100); + + JSTaggedValue doubleV((double)100.0); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, doubleV)); + EXPECT_EQ(result.GetDouble(), (double)100.0); + + JSTaggedValue undefinedV = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, undefinedV)); + EXPECT_TRUE(result.IsUndefined()); + + JSTaggedValue holeV = JSTaggedValue::Hole(); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, holeV)); + EXPECT_TRUE(result.IsHole()); + + JSTaggedValue nullV = JSTaggedValue::Null(); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, nullV)); + EXPECT_TRUE(result.IsNull()); + + JSTaggedValue falseV = JSTaggedValue::False(); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, falseV)); + EXPECT_TRUE(result.IsFalse()); + + JSTaggedValue trueV = JSTaggedValue::True(); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, trueV)); + EXPECT_TRUE(result.IsTrue()); +} + +HWTEST_F_L0(JSTaggedValueTest, ToBoolean) +{ + EXPECT_TRUE(JSTaggedValue(100).ToBoolean()); + EXPECT_FALSE(JSTaggedValue(0).ToBoolean()); + + EXPECT_TRUE(JSTaggedValue((double)100.0).ToBoolean()); + EXPECT_FALSE(JSTaggedValue(std::nan("")).ToBoolean()); + + EXPECT_FALSE(JSTaggedValue::Undefined().ToBoolean()); + + EXPECT_FALSE(JSTaggedValue::Hole().ToBoolean()); + + EXPECT_FALSE(JSTaggedValue::Null().ToBoolean()); + + EXPECT_FALSE(JSTaggedValue::False().ToBoolean()); + EXPECT_TRUE(JSTaggedValue::True().ToBoolean()); + + EXPECT_FALSE(thread->GetEcmaVM()->GetFactory()->GetEmptyString().GetTaggedValue().ToBoolean()); + EXPECT_TRUE(thread->GetEcmaVM()->GetFactory()->NewFromString("test").GetTaggedValue().ToBoolean()); +} + +HWTEST_F_L0(JSTaggedValueTest, ToNumber) +{ + JSTaggedNumber result; + + JSTaggedValue intV(100); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, intV)); + EXPECT_EQ(result.GetNumber(), 100); + + JSTaggedValue doubleV((double)100.0); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, doubleV)); + EXPECT_EQ(result.GetNumber(), (double)100.0); + + JSTaggedValue undefinedV = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, undefinedV)); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSTaggedValue holeV = JSTaggedValue::Hole(); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, holeV)); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSTaggedValue nullV = JSTaggedValue::Null(); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, nullV)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue falseV = JSTaggedValue::False(); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, falseV)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue trueV = JSTaggedValue::True(); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, trueV)); + EXPECT_EQ(result.GetNumber(), 1); + + JSHandle stringV0(thread->GetEcmaVM()->GetFactory()->NewFromString(" 1234 ")); + result = JSTaggedValue::ToNumber(thread, stringV0); + EXPECT_EQ(result.GetNumber(), 1234); + + JSHandle stringV1(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0b1010 ")); + result = JSTaggedValue::ToNumber(thread, stringV1); + EXPECT_EQ(result.GetNumber(), 10); + + JSHandle stringV2(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0O11 ")); + result = JSTaggedValue::ToNumber(thread, stringV2); + EXPECT_EQ(result.GetNumber(), 9); + + JSHandle stringV3(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0x2d ")); + result = JSTaggedValue::ToNumber(thread, stringV3); + EXPECT_EQ(result.GetNumber(), 45); + + JSHandle stringV4(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0.000001 ")); + result = JSTaggedValue::ToNumber(thread, stringV4); + EXPECT_EQ(result.GetNumber(), 0.000001); + + JSHandle stringV5(thread->GetEcmaVM()->GetFactory()->NewFromString(" 1.23 ")); + result = JSTaggedValue::ToNumber(thread, stringV5); + EXPECT_EQ(result.GetNumber(), 1.23); + + JSHandle stringV6(thread->GetEcmaVM()->GetFactory()->NewFromString(" -1.23e2 ")); + result = JSTaggedValue::ToNumber(thread, stringV6); + EXPECT_EQ(result.GetNumber(), -123); + + JSHandle stringV7(thread->GetEcmaVM()->GetFactory()->NewFromString(" -123e-2")); + result = JSTaggedValue::ToNumber(thread, stringV7); + EXPECT_EQ(result.GetNumber(), -1.23); + + JSHandle stringV8(thread->GetEcmaVM()->GetFactory()->NewFromString(" Infinity ")); + result = JSTaggedValue::ToNumber(thread, stringV8); + EXPECT_TRUE(std::isinf(result.GetNumber())); + + JSHandle stringV9(thread->GetEcmaVM()->GetFactory()->NewFromString("100e307")); + result = JSTaggedValue::ToNumber(thread, stringV9); + EXPECT_TRUE(std::isinf(result.GetNumber())); + + JSHandle stringV10(thread->GetEcmaVM()->GetFactory()->NewFromString(" .")); + result = JSTaggedValue::ToNumber(thread, stringV10); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle stringV11(thread->GetEcmaVM()->GetFactory()->NewFromString("12e+")); + result = JSTaggedValue::ToNumber(thread, stringV11); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle stringV12(thread->GetEcmaVM()->GetFactory()->NewFromString(".e3")); + result = JSTaggedValue::ToNumber(thread, stringV12); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle stringV13(thread->GetEcmaVM()->GetFactory()->NewFromString("23eE")); + result = JSTaggedValue::ToNumber(thread, stringV13); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle stringV14(thread->GetEcmaVM()->GetFactory()->NewFromString("a")); + result = JSTaggedValue::ToNumber(thread, stringV14); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle stringV15(thread->GetEcmaVM()->GetFactory()->NewFromString("0o12e3")); + result = JSTaggedValue::ToNumber(thread, stringV15); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle stringV16(thread->GetEcmaVM()->GetFactory()->NewFromString("0x12.3")); + result = JSTaggedValue::ToNumber(thread, stringV16); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle stringV17(thread->GetEcmaVM()->GetFactory()->NewFromString(" 12.4.")); + result = JSTaggedValue::ToNumber(thread, stringV17); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle stringV18(thread->GetEcmaVM()->GetFactory()->NewFromString("123test")); + result = JSTaggedValue::ToNumber(thread, stringV18); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle stringV19(thread->GetEcmaVM()->GetFactory()->NewFromString("123test")); + result = JSTaggedValue::ToNumber(thread, stringV19); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle stringV20(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0b ")); + result = JSTaggedValue::ToNumber(thread, stringV20); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle stringV21(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0b0000 ")); + result = JSTaggedValue::ToNumber(thread, stringV21); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle stringV22(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0o0000 ")); + result = JSTaggedValue::ToNumber(thread, stringV22); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle stringV23(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0X0000 ")); + result = JSTaggedValue::ToNumber(thread, stringV23); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle stringV24(thread->GetEcmaVM()->GetFactory()->NewFromString(" 000.00000 ")); + result = JSTaggedValue::ToNumber(thread, stringV24); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle stringV25(thread->GetEcmaVM()->GetFactory()->NewFromString("")); + result = JSTaggedValue::ToNumber(thread, stringV25); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle stringV26(thread->GetEcmaVM()->GetFactory()->NewFromString(" ")); + result = JSTaggedValue::ToNumber(thread, stringV26); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle stringV27(thread->GetEcmaVM()->GetFactory()->NewFromString("0")); + result = JSTaggedValue::ToNumber(thread, stringV27); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle stringV28(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0 ")); + result = JSTaggedValue::ToNumber(thread, stringV28); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle stringV29(thread->GetEcmaVM()->GetFactory()->NewFromString("00000000")); + result = JSTaggedValue::ToNumber(thread, stringV29); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle stringV30(thread->GetEcmaVM()->GetFactory()->NewFromString(" 00000000 ")); + result = JSTaggedValue::ToNumber(thread, stringV30); + EXPECT_EQ(result.GetNumber(), 0); + + thread->ClearException(); + JSHandle symbolV1(thread->GetEcmaVM()->GetFactory()->NewJSSymbol()); + JSTaggedValue::ToNumber(thread, symbolV1); + EXPECT_TRUE(thread->HasPendingException()); + EXPECT_TRUE(thread->GetException().IsJSError()); +} + +HWTEST_F_L0(JSTaggedValueTest, ToInteger) +{ + JSTaggedNumber result; + + JSTaggedValue intV(100); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, intV)); + EXPECT_EQ(result.GetNumber(), 100); + + JSTaggedValue doubleV1((double)100.0); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, doubleV1)); + EXPECT_EQ(result.GetNumber(), (double)100.0); + + JSTaggedValue doubleV2((double)100.123); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, doubleV2)); + EXPECT_EQ(result.GetNumber(), (double)100); + + JSTaggedValue undefinedV = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, undefinedV)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue holeV = JSTaggedValue::Hole(); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, holeV)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue nullV = JSTaggedValue::Null(); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, nullV)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue falseV = JSTaggedValue::False(); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, falseV)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue trueV = JSTaggedValue::True(); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, trueV)); + EXPECT_EQ(result.GetNumber(), 1); +} + +HWTEST_F_L0(JSTaggedValueTest, ToInt32) +{ + int32_t result; + + JSTaggedValue intV(100); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, intV)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV1((double)100.0); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, doubleV1)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV2((double)100.123); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, doubleV2)); + EXPECT_EQ(result, 100); + + double input1 = (static_cast(UINT32_MAX) + 1) + 12345; + JSTaggedValue doubleV3(input1); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, doubleV3)); + EXPECT_EQ(result, 12345); + + double input2 = 100 * (static_cast(UINT32_MAX) + 1) + 23456; + JSTaggedValue doubleV4(input2); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, doubleV4)); + EXPECT_EQ(result, 23456); + + double input3 = 100 * (static_cast(UINT32_MAX) + 1) + INT32_MAX + 1 + 23456; + JSTaggedValue doubleV5(input3); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, doubleV5)); + EXPECT_EQ(result, 23456 - static_cast(INT32_MAX) - 1); + + JSTaggedValue undefinedV = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, undefinedV)); + EXPECT_EQ(result, 0); + + JSTaggedValue holeV = JSTaggedValue::Hole(); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, holeV)); + EXPECT_EQ(result, 0); + + JSTaggedValue nullV = JSTaggedValue::Null(); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, nullV)); + EXPECT_EQ(result, 0); + + JSTaggedValue falseV = JSTaggedValue::False(); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, falseV)); + EXPECT_EQ(result, 0); + + JSTaggedValue trueV = JSTaggedValue::True(); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, trueV)); + EXPECT_EQ(result, 1); +} + +HWTEST_F_L0(JSTaggedValueTest, ToUint32) +{ + uint32_t result; + + JSTaggedValue intV(100); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, intV)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV1((double)100.0); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, doubleV1)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV2((double)100.123); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, doubleV2)); + EXPECT_EQ(result, 100); + + double input1 = (static_cast(UINT32_MAX) + 1) + 12345; + JSTaggedValue doubleV3(input1); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, doubleV3)); + EXPECT_EQ(result, 12345); + + double input2 = 100 * (static_cast(UINT32_MAX) + 1) + 23456; + JSTaggedValue doubleV4(input2); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, doubleV4)); + EXPECT_EQ(result, 23456); + + double input3 = 100 * (static_cast(UINT32_MAX) + 1) + INT32_MAX + 1 + 23456; + JSTaggedValue doubleV5(input3); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, doubleV5)); + EXPECT_EQ(result, static_cast(INT32_MAX) + 1 + 23456); + + JSTaggedValue undefinedV = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, undefinedV)); + EXPECT_EQ(result, 0); + + JSTaggedValue holeV = JSTaggedValue::Hole(); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, holeV)); + EXPECT_EQ(result, 0); + + JSTaggedValue nullV = JSTaggedValue::Null(); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, nullV)); + EXPECT_EQ(result, 0); + + JSTaggedValue falseV = JSTaggedValue::False(); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, falseV)); + EXPECT_EQ(result, 0); + + JSTaggedValue trueV = JSTaggedValue::True(); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, trueV)); + EXPECT_EQ(result, 1); +} + +HWTEST_F_L0(JSTaggedValueTest, ToInt16) +{ + int32_t result; + + JSTaggedValue intV(100); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, intV)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV1((double)100.0); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, doubleV1)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV2((double)100.123); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, doubleV2)); + EXPECT_EQ(result, 100); + + double input1 = (static_cast(UINT16_MAX) + 1) + 12345; + JSTaggedValue doubleV3(input1); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, doubleV3)); + EXPECT_EQ(result, 12345); + + double input2 = 100 * (static_cast(UINT16_MAX) + 1) + 23456; + JSTaggedValue doubleV4(input2); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, doubleV4)); + EXPECT_EQ(result, 23456); + + double input3 = 100 * (static_cast(UINT16_MAX) + 1) + INT16_MAX + 1 + 23456; + JSTaggedValue doubleV5(input3); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, doubleV5)); + EXPECT_EQ(result, 23456 - static_cast(INT16_MAX) - 1); + + JSTaggedValue undefinedV = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, undefinedV)); + EXPECT_EQ(result, 0); + + JSTaggedValue holeV = JSTaggedValue::Hole(); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, holeV)); + EXPECT_EQ(result, 0); + + JSTaggedValue nullV = JSTaggedValue::Null(); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, nullV)); + EXPECT_EQ(result, 0); + + JSTaggedValue falseV = JSTaggedValue::False(); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, falseV)); + EXPECT_EQ(result, 0); + + JSTaggedValue trueV = JSTaggedValue::True(); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, trueV)); + EXPECT_EQ(result, 1); +} + +HWTEST_F_L0(JSTaggedValueTest, ToUint16) +{ + uint32_t result; + + JSTaggedValue intV(100); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, intV)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV1((double)100.0); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, doubleV1)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV2((double)100.123); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, doubleV2)); + EXPECT_EQ(result, 100); + + double input1 = (static_cast(UINT16_MAX) + 1) + 12345; + JSTaggedValue doubleV3(input1); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, doubleV3)); + EXPECT_EQ(result, 12345); + + double input2 = 100 * (static_cast(UINT16_MAX) + 1) + 23456; + JSTaggedValue doubleV4(input2); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, doubleV4)); + EXPECT_EQ(result, 23456); + + double input3 = 100 * (static_cast(UINT16_MAX) + 1) + INT16_MAX + 1 + 23456; + JSTaggedValue doubleV5(input3); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, doubleV5)); + EXPECT_EQ(result, static_cast(INT16_MAX) + 1 + 23456); + + JSTaggedValue undefinedV = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, undefinedV)); + EXPECT_EQ(result, 0); + + JSTaggedValue holeV = JSTaggedValue::Hole(); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, holeV)); + EXPECT_EQ(result, 0); + + JSTaggedValue nullV = JSTaggedValue::Null(); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, nullV)); + EXPECT_EQ(result, 0); + + JSTaggedValue falseV = JSTaggedValue::False(); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, falseV)); + EXPECT_EQ(result, 0); + + JSTaggedValue trueV = JSTaggedValue::True(); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, trueV)); + EXPECT_EQ(result, 1); +} + +HWTEST_F_L0(JSTaggedValueTest, ToInt8) +{ + int32_t result; + + JSTaggedValue intV(100); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, intV)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV1((double)100.0); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, doubleV1)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV2((double)100.123); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, doubleV2)); + EXPECT_EQ(result, 100); + + double input1 = (static_cast(UINT8_MAX) + 1) + 45; + JSTaggedValue doubleV3(input1); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, doubleV3)); + EXPECT_EQ(result, 45); + + double input2 = 100 * (static_cast(UINT8_MAX) + 1) + 56; + JSTaggedValue doubleV4(input2); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, doubleV4)); + EXPECT_EQ(result, 56); + + double input3 = 100 * (static_cast(UINT8_MAX) + 1) + INT8_MAX + 1 + 23; + JSTaggedValue doubleV5(input3); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, doubleV5)); + EXPECT_EQ(result, 23 - static_cast(INT8_MAX) - 1); + + JSTaggedValue undefinedV = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, undefinedV)); + EXPECT_EQ(result, 0); + + JSTaggedValue holeV = JSTaggedValue::Hole(); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, holeV)); + EXPECT_EQ(result, 0); + + JSTaggedValue nullV = JSTaggedValue::Null(); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, nullV)); + EXPECT_EQ(result, 0); + + JSTaggedValue falseV = JSTaggedValue::False(); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, falseV)); + EXPECT_EQ(result, 0); + + JSTaggedValue trueV = JSTaggedValue::True(); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, trueV)); + EXPECT_EQ(result, 1); +} + +HWTEST_F_L0(JSTaggedValueTest, ToUint8) +{ + uint32_t result; + + JSTaggedValue intV(100); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, intV)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV1((double)100.0); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, doubleV1)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV2((double)100.123); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, doubleV2)); + EXPECT_EQ(result, 100); + + double input1 = (static_cast(UINT8_MAX) + 1) + 34; + JSTaggedValue doubleV3(input1); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, doubleV3)); + EXPECT_EQ(result, 34); + + double input2 = 100 * (static_cast(UINT8_MAX) + 1) + 45; + JSTaggedValue doubleV4(input2); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, doubleV4)); + EXPECT_EQ(result, 45); + + double input3 = 100 * (static_cast(UINT8_MAX) + 1) + INT8_MAX + 1 + 56; + JSTaggedValue doubleV5(input3); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, doubleV5)); + EXPECT_EQ(result, static_cast(INT8_MAX) + 1 + 56); + + JSTaggedValue undefinedV = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, undefinedV)); + EXPECT_EQ(result, 0); + + JSTaggedValue holeV = JSTaggedValue::Hole(); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, holeV)); + EXPECT_EQ(result, 0); + + JSTaggedValue nullV = JSTaggedValue::Null(); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, nullV)); + EXPECT_EQ(result, 0); + + JSTaggedValue falseV = JSTaggedValue::False(); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, falseV)); + EXPECT_EQ(result, 0); + + JSTaggedValue trueV = JSTaggedValue::True(); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, trueV)); + EXPECT_EQ(result, 1); +} + +HWTEST_F_L0(JSTaggedValueTest, ToUint8Clamp) +{ + uint32_t result; + + JSTaggedValue intV1(-100); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, intV1)); + EXPECT_EQ(result, 0); + + JSTaggedValue intV2(100); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, intV2)); + EXPECT_EQ(result, 100); + + JSTaggedValue intV3(300); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, intV3)); + EXPECT_EQ(result, 255); + + JSTaggedValue doubleV1((double)-100.123); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, doubleV1)); + EXPECT_EQ(result, 0); + + JSTaggedValue doubleV2((double)100.123); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, doubleV2)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV3((double)100.55); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, doubleV3)); + EXPECT_EQ(result, 101); + + JSTaggedValue doubleV4((double)99.9); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, doubleV4)); + EXPECT_EQ(result, 100); + + JSTaggedValue doubleV5((double)300.5); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, doubleV5)); + EXPECT_EQ(result, 255); + + JSTaggedValue undefinedV = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, undefinedV)); + EXPECT_EQ(result, 0); + + JSTaggedValue holeV = JSTaggedValue::Hole(); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, holeV)); + EXPECT_EQ(result, 0); + + JSTaggedValue nullV = JSTaggedValue::Null(); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, nullV)); + EXPECT_EQ(result, 0); + + JSTaggedValue falseV = JSTaggedValue::False(); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, falseV)); + EXPECT_EQ(result, 0); + + JSTaggedValue trueV = JSTaggedValue::True(); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, trueV)); + EXPECT_EQ(result, 1); +} + +HWTEST_F_L0(JSTaggedValueTest, ToPropertyKey) +{ + JSTaggedValue result; + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromString("null"); + JSTaggedValue key = str.GetTaggedValue(); + result = JSTaggedValue::ToPropertyKey(thread, JSHandle(thread, key)).GetTaggedValue(); + EXPECT_TRUE(key == result); +} + +void CheckOkString(JSThread *thread, const JSHandle &tagged, CString &rightCStr) +{ + JSHandle result = JSTaggedValue::ToString(thread, tagged); + JSHandle rightString = thread->GetEcmaVM()->GetFactory()->NewFromString(rightCStr); + EXPECT_TRUE(EcmaString::StringsAreEqual(EcmaString::Cast(result.GetObject()), + EcmaString::Cast(rightString.GetObject()))); +} + +HWTEST_F_L0(JSTaggedValueTest, ToString) +{ + CString rightCStr = ""; + CheckOkString(thread, JSHandle(thread, JSTaggedValue()), rightCStr); + + rightCStr = "undefined"; + CheckOkString(thread, JSHandle(thread, JSTaggedValue::Undefined()), rightCStr); + + rightCStr = "null"; + CheckOkString(thread, JSHandle(thread, JSTaggedValue::Null()), rightCStr); + + rightCStr = "true"; + CheckOkString(thread, JSHandle(thread, JSTaggedValue::True()), rightCStr); + + rightCStr = "false"; + CheckOkString(thread, JSHandle(thread, JSTaggedValue::False()), rightCStr); + + rightCStr = "hello world"; + CheckOkString(thread, JSHandle(thread->GetEcmaVM()->GetFactory()->NewFromString(rightCStr)), + rightCStr); + + double num = 1; + JSTaggedNumber numberNum = JSTaggedNumber(num); + rightCStr = "1"; + CheckOkString(thread, JSHandle(thread, numberNum), rightCStr); + + num = 1.23; + numberNum = JSTaggedNumber(num); + rightCStr = "1.23"; + CheckOkString(thread, JSHandle(thread, numberNum), rightCStr); + + int numInt = 2; + JSHandle value1(thread, JSTaggedValue(numInt)); + rightCStr = "2"; + CheckOkString(thread, JSHandle::Cast(JSTaggedValue::ToObject(thread, value1)), rightCStr); + + num = 1.23; + JSHandle value2(thread, JSTaggedValue(num)); + rightCStr = "1.23"; + CheckOkString(thread, JSHandle::Cast(JSTaggedValue::ToObject(thread, value2)), rightCStr); + + bool valueBool = true; + JSHandle value3(thread, JSTaggedValue(valueBool)); + rightCStr = "true"; + CheckOkString(thread, JSHandle::Cast(JSTaggedValue::ToObject(thread, value3)), rightCStr); +} + +HWTEST_F_L0(JSTaggedValueTest, CanonicalNumericIndexString) +{ + JSTaggedValue result; + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromString("-0"); + JSTaggedValue tmpStr = str.GetTaggedValue(); + result = JSTaggedValue::CanonicalNumericIndexString(thread, JSHandle(thread, tmpStr)); + EXPECT_EQ(result.GetDouble(), -0.0); + + JSTaggedValue tmpInt(1); + result = JSTaggedValue::CanonicalNumericIndexString(thread, JSHandle(thread, tmpInt)); + EXPECT_TRUE(result.IsUndefined()); + + JSTaggedValue tmpDouble((double)100.0); + result = JSTaggedValue::CanonicalNumericIndexString(thread, JSHandle(thread, tmpDouble)); + EXPECT_TRUE(result.IsUndefined()); +} + +HWTEST_F_L0(JSTaggedValueTest, ToObject) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle value1(thread, JSTaggedValue(2)); + JSTaggedValue tagged1 = + JSTaggedValue(JSHandle::Cast(JSTaggedValue::ToObject(thread, value1))->GetValue()); + EXPECT_EQ(tagged1.GetRawData(), JSTaggedValue(2).GetRawData()); + + JSHandle value2(thread, JSTaggedValue(2.2)); + JSTaggedValue tagged2 = + JSTaggedValue(JSHandle::Cast(JSTaggedValue::ToObject(thread, value2))->GetValue()); + EXPECT_EQ(tagged2.GetRawData(), JSTaggedValue(static_cast(2.2)).GetRawData()); + + JSHandle value3(thread, JSTaggedValue::True()); + JSTaggedValue tagged3 = + JSTaggedValue(JSHandle::Cast(JSTaggedValue::ToObject(thread, value3))->GetValue()); + EXPECT_EQ(tagged3.GetRawData(), JSTaggedValue::True().GetRawData()); + + JSHandle value4(factory->NewFromString("aaa")); + JSTaggedValue tagged4 = + JSTaggedValue(JSHandle::Cast(JSTaggedValue::ToObject(thread, value4))->GetValue()); + EXPECT_TRUE(tagged4.IsString()); + EXPECT_EQ(reinterpret_cast(tagged4.GetRawData())->Compare(value4.GetObject()), 0); + + JSHandle symbol = factory->NewPublicSymbolWithChar("bbb"); + JSHandle str = factory->NewFromString("bbb"); + JSHandle value5(symbol); + JSTaggedValue tagged5 = + JSTaggedValue(JSHandle::Cast(JSTaggedValue::ToObject(thread, value5))->GetValue()); + EXPECT_EQ(EcmaString::Cast(reinterpret_cast(tagged5.GetRawData())->GetDescription().GetTaggedObject()) + ->Compare(*str), + 0); + EXPECT_TRUE(tagged5.IsSymbol()); + + EcmaVM *ecma = thread->GetEcmaVM(); + JSHandle objectFun = ecma->GetGlobalEnv()->GetObjectFunction(); + JSHandle jsObj = factory->NewJSObjectByConstructor(JSHandle(objectFun), objectFun); + JSHandle value(jsObj); + EXPECT_EQ(*JSTaggedValue::ToObject(thread, value), *jsObj); +} + +HWTEST_F_L0(JSTaggedValueTest, ToLength) +{ + JSTaggedNumber result; + + JSTaggedValue intV(100); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, intV)); + EXPECT_EQ(result.GetNumber(), 100); + + JSTaggedValue intV2(-1); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, intV2)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue doubleV1((double)100.0); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, doubleV1)); + EXPECT_EQ(result.GetNumber(), (double)100.0); + + JSTaggedValue doubleV2((double)100.123); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, doubleV2)); + EXPECT_EQ(result.GetNumber(), (double)100); + + JSTaggedValue doubleV3((double)-1.0); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, doubleV3)); + EXPECT_EQ(result.GetNumber(), (double)0); + + JSTaggedValue doubleV4((double)9007199254740992); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, doubleV4)); + EXPECT_EQ(result.GetNumber(), (double)9007199254740991); + + JSTaggedValue undefinedV = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, undefinedV)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue holeV = JSTaggedValue::Hole(); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, holeV)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue nullV = JSTaggedValue::Null(); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, nullV)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue falseV = JSTaggedValue::False(); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, falseV)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue trueV = JSTaggedValue::True(); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, trueV)); + EXPECT_EQ(result.GetNumber(), 1); +} + +HWTEST_F_L0(JSTaggedValueTest, IsArray) +{ + EcmaVM *ecma = thread->GetEcmaVM(); + JSHandle objectFun = ecma->GetGlobalEnv()->GetArrayFunction(); + + JSHandle jsObj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objectFun), objectFun); + + ASSERT_TRUE(jsObj->IsJSArray()); + ASSERT_FALSE(JSTaggedValue(1).IsArray(thread)); + + ASSERT_FALSE(thread->GetEcmaVM()->GetFactory()->NewFromString("test").GetTaggedValue().IsArray(thread)); +} + +HWTEST_F_L0(JSTaggedValueTest, IsCallable_IsConstructor_IsExtensible) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle jsFunction = thread->GetEcmaVM()->GetFactory()->NewJSFunction(env); + JSHClass *jsHclass = jsFunction->GetJSHClass(); + jsHclass->SetCallable(true); + ASSERT_TRUE(jsFunction->IsCallable()); + jsHclass->SetCallable(false); + ASSERT_FALSE(jsFunction->IsCallable()); + jsHclass->SetConstructor(true); + ASSERT_TRUE(jsFunction->IsConstructor()); + jsHclass->SetConstructor(false); + ASSERT_FALSE(jsFunction->IsConstructor()); + jsHclass->SetExtensible(true); + ASSERT_TRUE(jsFunction->IsExtensible()); + jsHclass->SetExtensible(false); + ASSERT_FALSE(jsFunction->IsExtensible()); + ASSERT_FALSE(JSTaggedValue(1).IsExtensible(thread)); + ASSERT_FALSE(JSTaggedValue(1).IsConstructor()); + ASSERT_FALSE(JSTaggedValue(1).IsCallable()); +} + +HWTEST_F_L0(JSTaggedValueTest, IsInteger) +{ + ASSERT_TRUE(JSTaggedValue(1).IsInteger()); + ASSERT_TRUE(JSTaggedValue(1.0).IsInteger()); + ASSERT_TRUE(JSTaggedValue(-1.0).IsInteger()); + ASSERT_FALSE(JSTaggedValue(-1.1).IsInteger()); + ASSERT_FALSE(JSTaggedValue(1.1).IsInteger()); + ASSERT_FALSE(JSTaggedValue(std::numeric_limits::infinity()).IsInteger()); + ASSERT_FALSE(JSTaggedValue((-1) * std::numeric_limits::infinity()).IsInteger()); + ASSERT_TRUE(JSTaggedValue(0).IsInteger()); + ASSERT_TRUE(JSTaggedValue(0.0).IsInteger()); + ASSERT_FALSE(JSTaggedValue::True().IsInteger()); + ASSERT_FALSE(JSTaggedValue::Undefined().IsInteger()); + ASSERT_FALSE(JSTaggedValue::Null().IsInteger()); + ASSERT_FALSE(JSTaggedValue::False().IsInteger()); + ASSERT_FALSE(JSTaggedValue::Hole().IsInteger()); + ASSERT_FALSE(thread->GetEcmaVM()->GetFactory()->NewFromString("test").GetTaggedValue().IsInteger()); +} + +HWTEST_F_L0(JSTaggedValueTest, IsPropertyKey) +{ + ASSERT_TRUE(JSTaggedValue::IsPropertyKey( + JSHandle(thread->GetEcmaVM()->GetFactory()->NewFromString("test")))); +} + +HWTEST_F_L0(JSTaggedValueTest, IsRegExp) +{ + JSHandle string = thread->GetEcmaVM()->GetFactory()->NewFromString("test"); + JSHandle obj = JSHandle::Cast(string); + ASSERT_FALSE(JSObject::IsRegExp(thread, obj)); +} + +HWTEST_F_L0(JSTaggedValueTest, SameValue) +{ + EcmaVM *ecma = thread->GetEcmaVM(); + JSHandle objectFun = ecma->GetGlobalEnv()->GetObjectFunction(); + + JSHandle jsObj = + ecma->GetFactory()->NewJSObjectByConstructor(JSHandle(objectFun), objectFun); + + // not same type + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1), JSTaggedValue::False())); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1.0), JSTaggedValue::True())); + ASSERT_FALSE( + JSTaggedValue::SameValue(JSTaggedValue(1), ecma->GetFactory()->NewFromString("test").GetTaggedValue())); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1), JSTaggedValue(*jsObj))); + JSHandle test(ecma->GetFactory()->NewFromString("test")); + ASSERT_FALSE(JSTaggedValue::SameValue(test.GetTaggedValue(), JSTaggedValue(*jsObj))); + + // number compare + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(1), JSTaggedValue(1))); + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(1), JSTaggedValue(1.0))); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1.0), JSTaggedValue(2.0))); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(std::numeric_limits::quiet_NaN()), JSTaggedValue(2.0))); + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(std::numeric_limits::quiet_NaN()), + JSTaggedValue(std::numeric_limits::quiet_NaN()))); + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(std::numeric_limits::quiet_NaN()), + JSTaggedValue(std::numeric_limits::quiet_NaN()))); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(0.0), JSTaggedValue(-0.0))); + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(0), JSTaggedValue(-0))); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1.0), JSTaggedValue(-1.0))); + + // string compare + JSHandle test1(ecma->GetFactory()->NewFromString("test1")); + ASSERT_FALSE(JSTaggedValue::SameValue(test.GetTaggedValue(), test1.GetTaggedValue())); + ASSERT_TRUE(JSTaggedValue::SameValue(test.GetTaggedValue(), test.GetTaggedValue())); + + // bool compare + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue::True(), JSTaggedValue::True())); + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue::False(), JSTaggedValue::False())); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue::False(), JSTaggedValue::True())); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue::True(), JSTaggedValue::False())); + + // js object compare + ASSERT_TRUE(JSTaggedValue::SameValue(jsObj.GetTaggedValue(), jsObj.GetTaggedValue())); + + // undefined or null compare + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue::Undefined(), JSTaggedValue::Undefined())); + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue::Null(), JSTaggedValue::Null())); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue::Undefined(), JSTaggedValue::Null())); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue::Null(), JSTaggedValue::Undefined())); +} + +HWTEST_F_L0(JSTaggedValueTest, SameValueZero) +{ + // SameValueZero differs from SameValue only in its treatment of +0 and -0. + ASSERT_TRUE(JSTaggedValue::SameValueZero(JSTaggedValue(0.0), JSTaggedValue(-0.0))); +} + +HWTEST_F_L0(JSTaggedValueTest, Less) +{ + JSHandle test(thread->GetEcmaVM()->GetFactory()->NewFromString("test")); + JSHandle test1(thread->GetEcmaVM()->GetFactory()->NewFromString("test1")); + JSHandle test2(thread->GetEcmaVM()->GetFactory()->NewFromString("test2")); + + ASSERT_TRUE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue(1.0)), + JSHandle(thread, JSTaggedValue(2.0)))); + ASSERT_FALSE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue(0.0)), + JSHandle(thread, JSTaggedValue(-0.0)))); + ASSERT_TRUE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue(1)), + JSHandle(thread, JSTaggedValue(2)))); + + ASSERT_TRUE(JSTaggedValue::Less(thread, test, test1)); + ASSERT_FALSE(JSTaggedValue::Less(thread, test2, test1)); + ASSERT_FALSE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue(1)), test1)); + ASSERT_FALSE(JSTaggedValue::Less(thread, test2, JSHandle(thread, JSTaggedValue(2)))); + + ASSERT_TRUE(JSTaggedValue::Less(thread, + JSHandle(thread->GetEcmaVM()->GetFactory()->NewFromString("1")), + JSHandle(thread, JSTaggedValue(2)))); + ASSERT_TRUE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue(1)), + JSHandle(thread->GetEcmaVM()->GetFactory()->NewFromString("2")))); + + ASSERT_TRUE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue::True()))); + ASSERT_FALSE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue::False()))); + ASSERT_FALSE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue::Undefined()), + JSHandle(thread, JSTaggedValue::Null()))); + ASSERT_FALSE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue::Undefined()))); +} + +HWTEST_F_L0(JSTaggedValueTest, Equal) +{ + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Undefined()), + JSHandle(thread, JSTaggedValue::Undefined()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue::Undefined()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue::Null()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue::Undefined()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue(1)))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue::True()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue::False()))); + + // number compare + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(1)), + JSHandle(thread, JSTaggedValue(1)))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(1)), + JSHandle(thread, JSTaggedValue(2)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(1.0)), + JSHandle(thread, JSTaggedValue(1.0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0.0)), + JSHandle(thread, JSTaggedValue(0.0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0.0)), + JSHandle(thread, JSTaggedValue(-0.0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0)), + JSHandle(thread, JSTaggedValue(-0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(1)), + JSHandle(thread, JSTaggedValue::True()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0)), + JSHandle(thread, JSTaggedValue::False()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0)), + JSHandle(thread, JSTaggedValue::Undefined()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0)), + JSHandle(thread, JSTaggedValue::Null()))); + + JSHandle test(thread->GetEcmaVM()->GetFactory()->NewFromString("test")); + JSHandle test1(thread->GetEcmaVM()->GetFactory()->NewFromString("test1")); + JSHandle empty(thread->GetEcmaVM()->GetFactory()->NewFromString("")); + JSHandle char0(thread->GetEcmaVM()->GetFactory()->NewFromString("0")); + JSHandle char0Point0(thread->GetEcmaVM()->GetFactory()->NewFromString("0.0")); + JSHandle char1(thread->GetEcmaVM()->GetFactory()->NewFromString("1")); + JSHandle char1Point0(thread->GetEcmaVM()->GetFactory()->NewFromString("1.0")); + JSHandle charM1(thread->GetEcmaVM()->GetFactory()->NewFromString("-1")); + JSHandle charM0Point0(thread->GetEcmaVM()->GetFactory()->NewFromString("-0.0")); + JSHandle charM0Point1(thread->GetEcmaVM()->GetFactory()->NewFromString("-0.1")); + + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0)), char0)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0.0)), char0)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(1.0)), char1)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(-1.0)), charM1)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0.0)), charM0Point0)); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0.0)), charM0Point1)); + + // string compare + ASSERT_TRUE(JSTaggedValue::Equal(thread, test, test)); + ASSERT_FALSE(JSTaggedValue::Equal(thread, test, test1)); + ASSERT_FALSE(JSTaggedValue::Equal(thread, test, empty)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, empty, empty)); + + + ASSERT_TRUE(JSTaggedValue::Equal(thread, char1, JSHandle(thread, JSTaggedValue(1)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, char1, JSHandle(thread, JSTaggedValue::True()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, char0, JSHandle(thread, JSTaggedValue::False()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, char0, JSHandle(thread, JSTaggedValue::Undefined()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, char0, JSHandle(thread, JSTaggedValue::Null()))); + + // boolean compare + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue::True()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue::False()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue::False()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue::True()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue(1)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue(1.0)))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue(0)))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue(0.0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue(0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue(0.0)))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue(1)))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue(1.0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), char0)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), char0Point0)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), char1)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), char1Point0)); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), char0)); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), char1)); +} + +HWTEST_F_L0(JSTaggedValueTest, StrictEqual) +{ + // This algorithm differs from the SameValue Algorithm in its treatment of signed zeroes and NaNs. + ASSERT_TRUE(JSTaggedValue::StrictEqual(thread, JSHandle(thread, JSTaggedValue(0.0)), + JSHandle(thread, JSTaggedValue(-0.0)))); + ASSERT_FALSE(JSTaggedValue::StrictEqual( + thread, JSHandle(thread, JSTaggedValue(std::numeric_limits::quiet_NaN())), + JSHandle(thread, JSTaggedValue(std::numeric_limits::quiet_NaN())))); +} +} // namespace panda::test diff --git a/ecmascript/tests/test_helper.h b/ecmascript/tests/test_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..6d3cd4c088065a9de099ea0063e08c0f9fd1d696 --- /dev/null +++ b/ecmascript/tests/test_helper.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/interpreter/interpreter.h" +#include "ecmascript/ecma_language_context.h" +#include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/object_factory.h" +#include "gtest/gtest.h" +#include "include/runtime_options.h" + +namespace panda::test { +using panda::ecmascript::EcmaHandleScope; +using panda::ecmascript::EcmaRuntimeCallInfo; +using panda::ecmascript::EcmaVM; +using panda::ecmascript::FrameState; +using panda::ecmascript::JSTaggedType; +using panda::ecmascript::JSTaggedValue; +using panda::ecmascript::JSThread; +using panda::ecmascript::NUM_MANDATORY_JSFUNC_ARGS; + +// Add for hmf tests platform, define to TEST_F or TEST_P when running gtest in gitlab +#define HWTEST_F_L0(testsuit, testcase) HWTEST_F(testsuit, testcase, testing::ext::TestSize.Level0) +#define HWTEST_P_L0(testsuit, testcase) HWTEST_P(testsuit, testcase, testing::ext::TestSize.Level0) + +class TestHelper { +public: + static std::unique_ptr CreateEcmaRuntimeCallInfo(JSThread *thread, JSTaggedValue newTgt, + array_size_t argvLength) + { + const uint8_t testDecodedSize = 2; + // argvLength includes number of int64_t to store value and tag of function, 'this' and call args + // It doesn't include new.target argument + uint32_t numActualArgs = argvLength / testDecodedSize + 1; + JSTaggedType *sp = thread->GetCurrentSPFrame(); + size_t frameSize = ecmascript::FRAME_STATE_SIZE + numActualArgs; + JSTaggedType *newSp = sp - frameSize; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + auto callInfo = + std::make_unique(thread, numActualArgs, reinterpret_cast(newSp)); + callInfo->SetNewTarget(newTgt); + return callInfo; + } + + static JSTaggedType *SetupFrame(JSThread *thread, EcmaRuntimeCallInfo *info) + { + JSTaggedType *sp = thread->GetCurrentSPFrame(); + size_t frameSize = ecmascript::FRAME_STATE_SIZE + info->GetArgsNumber() + NUM_MANDATORY_JSFUNC_ARGS; + JSTaggedType *newSp = sp - frameSize; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + FrameState *state = reinterpret_cast(newSp) - 1; + state->prev = sp; + state->pc = nullptr; + state->sp = newSp; + state->method = thread->GetEcmaVM()->GetMethodForNativeFunction(nullptr); + thread->SetCurrentSPFrame(newSp); + return sp; + } + + static void TearDownFrame(JSThread *thread, JSTaggedType *prev) + { + thread->SetCurrentSPFrame(prev); + } + + // If you want to call once create, you can refer to BuiltinsMathTest for detail. + static void CreateEcmaVMWithScope(PandaVM *&instance, JSThread *&thread, EcmaHandleScope *&scope) + { + RuntimeOptions options; + options.SetShouldLoadBootPandaFiles(false); + options.SetShouldInitializeIntrinsics(false); + options.SetBootClassSpaces({"ecmascript"}); + options.SetRuntimeType("ecmascript"); + options.SetPreGcHeapVerifyEnabled(true); + static EcmaLanguageContext lcEcma; + [[maybe_unused]] bool success = Runtime::Create(options, {&lcEcma}); + ASSERT_TRUE(success) << "Cannot create Runtime"; + instance = Runtime::GetCurrent()->GetPandaVM(); + ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM"; + thread = EcmaVM::Cast(instance)->GetJSThread(); + scope = new EcmaHandleScope(thread); + thread->SetIsEcmaInterpreter(true); + EcmaVM::Cast(instance)->GetFactory()->SetTriggerGc(true); + } + + static inline void DestroyEcmaVMWithScope(PandaVM *instance, EcmaHandleScope *scope) + { + delete scope; + EcmaVM::Cast(instance)->GetFactory()->SetTriggerGc(false); + auto thread = EcmaVM::Cast(instance)->GetJSThread(); + thread->ClearException(); + [[maybe_unused]] bool success = Runtime::Destroy(); + ASSERT_TRUE(success) << "Cannot destroy Runtime"; + } +}; +} // namespace panda::test diff --git a/ecmascript/tests/weak_ref_gen_gc_test.cpp b/ecmascript/tests/weak_ref_gen_gc_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64baccd985a30097d499cf8c92ce4700e67f83e3 --- /dev/null +++ b/ecmascript/tests/weak_ref_gen_gc_test.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class WeakRefGenGCTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +#if !defined(NDEBUG) +static JSObject *JSObjectTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + EcmaVM *ecmaVM = thread->GetEcmaVM(); + auto globalEnv = ecmaVM->GetGlobalEnv(); + JSFunction *jsFunc = globalEnv->GetObjectFunction().GetObject(); + JSHandle jsFunc1(thread, jsFunc); + JSHandle newObj = + ecmaVM->GetFactory()->NewJSObjectByConstructor(JSHandle(jsFunc1), jsFunc1); + return *newObj; +} +#endif + +#if !defined(NDEBUG) +static TaggedArray *ArrayTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + // 2 : test case + JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(2); + return *array; +} +#endif + +HWTEST_F_L0(WeakRefGenGCTest, ArrayNonMovable) +{ +#if !defined(NDEBUG) + auto vm = thread->GetEcmaVM(); + auto array = vm->GetFactory()->NewTaggedArray(2, JSTaggedValue::Undefined(), true); + JSHandle newObj1(thread, JSObjectTestCreate(thread)); + array->Set(thread, 0, newObj1.GetTaggedValue()); + + JSObject *newObj2 = JSObjectTestCreate(thread); + JSTaggedValue value(newObj2); + value.CreateWeakRef(); + array->Set(thread, 1, value); + EXPECT_EQ(newObj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(value, array->Get(1)); + vm->CollectGarbage(TriggerGCType::SEMI_GC); + EXPECT_EQ(newObj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(JSTaggedValue::Undefined(), array->Get(1)); +#endif +} + +HWTEST_F_L0(WeakRefGenGCTest, ArrayUndefined) +{ +#if !defined(NDEBUG) + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle array = ecmaVM->GetFactory()->NewTaggedArray(2); + EXPECT_TRUE(*array != nullptr); + JSHandle newObj1(thread, JSObjectTestCreate(thread)); + array->Set(thread, 0, newObj1.GetTaggedValue()); + + JSObject *newObj2 = JSObjectTestCreate(thread); + JSTaggedValue value(newObj2); + value.CreateWeakRef(); + array->Set(thread, 1, value); + EXPECT_EQ(newObj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(value, array->Get(1)); + ecmaVM->CollectGarbage(TriggerGCType::SEMI_GC); + EXPECT_EQ(newObj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(JSTaggedValue::Undefined(), array->Get(1)); +#endif +} + +HWTEST_F_L0(WeakRefGenGCTest, ArrayKeep) +{ +#if !defined(NDEBUG) + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle array = ecmaVM->GetFactory()->NewTaggedArray(2); + EXPECT_TRUE(*array != nullptr); + JSHandle newObj1(thread, JSObjectTestCreate(thread)); + array->Set(thread, 0, newObj1.GetTaggedValue()); + + JSHandle newObj2(thread, JSObjectTestCreate(thread)); + JSTaggedValue value(newObj2.GetTaggedValue()); + value.CreateWeakRef(); + array->Set(thread, 1, value); + EXPECT_EQ(newObj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(value, array->Get(1)); + ecmaVM->CollectGarbage(TriggerGCType::SEMI_GC); + EXPECT_EQ(newObj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(true, array->Get(1).IsWeak()); + value = newObj2.GetTaggedValue(); + value.CreateWeakRef(); + EXPECT_EQ(value, array->Get(1)); +#endif +} + +HWTEST_F_L0(WeakRefGenGCTest, DynObjectUndefined) +{ +#if !defined(NDEBUG) + JSHandle newObj1(thread, JSObjectTestCreate(thread)); + JSTaggedValue array(ArrayTestCreate(thread)); + array.CreateWeakRef(); + newObj1->SetElements(thread, array); + EXPECT_EQ(newObj1->GetElements(), array); + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::SEMI_GC); + EXPECT_EQ(newObj1->GetElements(), JSTaggedValue::Undefined()); +#endif +} + +HWTEST_F_L0(WeakRefGenGCTest, DynObjectKeep) +{ +#if !defined(NDEBUG) + JSHandle newObj1(thread, JSObjectTestCreate(thread)); + JSHandle array(thread, ArrayTestCreate(thread)); + JSTaggedValue value = array.GetTaggedValue(); + value.CreateWeakRef(); + newObj1->SetElements(thread, value); + EXPECT_EQ(newObj1->GetElements(), value); + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::SEMI_GC); + value = array.GetTaggedValue(); + value.CreateWeakRef(); + EXPECT_EQ(newObj1->GetElements(), value); +#endif +} +} // namespace panda::test diff --git a/ecmascript/tests/weak_ref_stw_gc_test.cpp b/ecmascript/tests/weak_ref_stw_gc_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b1e438874f1211b36027eba22ee40578bb605c48 --- /dev/null +++ b/ecmascript/tests/weak_ref_stw_gc_test.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class WeakRefStwGCTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +#if !defined(NDEBUG) +static JSObject *JSObjectTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + EcmaVM *ecmaVM = thread->GetEcmaVM(); + auto globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle jsFunc = globalEnv->GetObjectFunction(); + JSHandle newObj = + ecmaVM->GetFactory()->NewJSObjectByConstructor(JSHandle(jsFunc), jsFunc); + return *newObj; +} +#endif + +#if !defined(NDEBUG) +static TaggedArray *ArrayTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + // 2 : test case + JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(2); + return *array; +} +#endif + +HWTEST_F_L0(WeakRefStwGCTest, ArrayUndefined) +{ +#if !defined(NDEBUG) + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle array = ecmaVM->GetFactory()->NewTaggedArray(2); + EXPECT_TRUE(*array != nullptr); + JSHandle newObj1(thread, JSObjectTestCreate(thread)); + array->Set(thread, 0, newObj1.GetTaggedValue()); + + JSObject *newObj2 = JSObjectTestCreate(thread); + JSTaggedValue value(newObj2); + value.CreateWeakRef(); + array->Set(thread, 1, value); + EXPECT_EQ(newObj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(value, array->Get(1)); + ecmaVM->CollectGarbage(TriggerGCType::SEMI_GC); + EXPECT_EQ(newObj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(JSTaggedValue::Undefined(), array->Get(1)); +#endif +} + +HWTEST_F_L0(WeakRefStwGCTest, ArrayKeep) +{ +#if !defined(NDEBUG) + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle array = ecmaVM->GetFactory()->NewTaggedArray(2); + EXPECT_TRUE(*array != nullptr); + JSHandle newObj1(thread, JSObjectTestCreate(thread)); + array->Set(thread, 0, newObj1.GetTaggedValue()); + + JSHandle newObj2(thread, JSObjectTestCreate(thread)); + JSTaggedValue value(newObj2.GetTaggedValue()); + value.CreateWeakRef(); + array->Set(thread, 1, value); + EXPECT_EQ(newObj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(value, array->Get(1)); + ecmaVM->CollectGarbage(TriggerGCType::SEMI_GC); + EXPECT_EQ(newObj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(true, array->Get(1).IsWeak()); + value = newObj2.GetTaggedValue(); + value.CreateWeakRef(); + EXPECT_EQ(value, array->Get(1)); +#endif +} + +HWTEST_F_L0(WeakRefStwGCTest, DynObjectUndefined) +{ +#if !defined(NDEBUG) + JSHandle newObj1(thread, JSObjectTestCreate(thread)); + JSTaggedValue array(ArrayTestCreate(thread)); + array.CreateWeakRef(); + newObj1->SetElements(thread, array); + EXPECT_EQ(newObj1->GetElements(), array); + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::SEMI_GC); + EXPECT_EQ(newObj1->GetElements(), JSTaggedValue::Undefined()); +#endif +} + +HWTEST_F_L0(WeakRefStwGCTest, DynObjectKeep) +{ +#if !defined(NDEBUG) + JSHandle newObj1(thread, JSObjectTestCreate(thread)); + JSHandle array(thread, ArrayTestCreate(thread)); + JSTaggedValue value = array.GetTaggedValue(); + value.CreateWeakRef(); + newObj1->SetElements(thread, value); + EXPECT_EQ(newObj1->GetElements(), value); + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::SEMI_GC); + value = array.GetTaggedValue(); + value.CreateWeakRef(); + EXPECT_EQ(newObj1->GetElements(), value); +#endif +} +} // namespace panda::test diff --git a/ecmascript/thread/thread_pool.h b/ecmascript/thread/thread_pool.h new file mode 100644 index 0000000000000000000000000000000000000000..8004a7023c56588e14312faf0056ec80714290ba --- /dev/null +++ b/ecmascript/thread/thread_pool.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_ECMASCRIPT_THREAD_THREAD_POOL_H +#define PANDA_ECMASCRIPT_THREAD_THREAD_POOL_H + +#include +#include +#include +#include + +#include "os/mutex.h" +#include "os/thread.h" +#include "ecmascript/thread/thread_safe_queue.h" +#include "libpandabase/utils/logger.h" + +namespace panda::ecmascript { +using ThreadPoolTask = std::function; + +class ThreadPool { +public: + explicit ThreadPool(size_t threadNum) : stop_(false), threadNum_(threadNum) + { + for (size_t i = 1; i < threadNum; i++) { + workers_.emplace_back([this, i]() { + for (;;) { + std::function task; + { + os::memory::LockHolder lock(mtx_); + // NOLINTNEXTLINE(readability-implicit-bool-conversion) + while (!stop_ && tasks_.empty()) { + cv_.Wait(&mtx_); + } + + if (stop_ && tasks_.empty()) { + return; + } + + tasks_.dequeue(task); + } + task(i); + { + os::memory::LockHolder lock(mtx_); + taskingCount_--; + if (taskingCount_ == 0) { + barrierCv_.Signal(); + } + } + } + }); + os::thread::SetThreadName(workers_.at(i - 1).native_handle(), ("js-GcTask" + std::to_string(i)).c_str()); + } + } + + ~ThreadPool() + { + { + os::memory::LockHolder lock(mtx_); + stop_ = true; + } + cv_.SignalAll(); + for (auto &worker : workers_) { + if (worker.joinable()) { + worker.join(); + } + } + } + + void Submit(const ThreadPoolTask &taskPtr) + { + if (stop_) { + LOG(FATAL, RUNTIME) << "submit on stopped thread_pool"; + } + std::function wrapperFunc = [taskPtr](uint32_t threadId) { taskPtr(threadId); }; + volatile auto atomicField = reinterpret_cast *>(&taskingCount_); + uint32_t oldTaskCount; + do { + oldTaskCount = atomicField->load(std::memory_order_acquire); + } while (!std::atomic_compare_exchange_strong_explicit(atomicField, &oldTaskCount, oldTaskCount + 1, + std::memory_order_release, std::memory_order_relaxed)); + tasks_.enqueue(wrapperFunc); + cv_.Signal(); + } + + void WaitTaskFinish() + { + os::memory::LockHolder lock(mtx_); + while (taskingCount_ != 0) { + barrierCv_.Wait(&mtx_); + } + } + + uint32_t GetTaskCount() + { + return reinterpret_cast *>(&taskingCount_)->load(std::memory_order_acquire); + } + + uint32_t GetThreadNum() const + { + return threadNum_; + } + +private: + NO_COPY_SEMANTIC(ThreadPool); + NO_MOVE_SEMANTIC(ThreadPool); + + bool stop_; + uint32_t threadNum_{0}; + std::atomic taskingCount_{0}; + std::vector workers_; + ThreadSafeQueue> tasks_; + os::memory::Mutex mtx_; + os::memory::ConditionVariable cv_; + os::memory::ConditionVariable barrierCv_; +}; +} // namespace panda::ecmascript + +#endif // PANDA_ECMASCRIPT_THREAD_THREAD_POOL_H diff --git a/ecmascript/thread/thread_safe_queue.h b/ecmascript/thread/thread_safe_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..7fff073b13101633b2ffd4899002ff545af5a33e --- /dev/null +++ b/ecmascript/thread/thread_safe_queue.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_ECMASCRIPT_THREAD_THREAD_SAFE_QUEUE_H +#define PANDA_ECMASCRIPT_THREAD_THREAD_SAFE_QUEUE_H + +#include "os/mutex.h" +#include + +namespace panda::ecmascript { +template +class ThreadSafeQueue { +public: + explicit ThreadSafeQueue() = default; + ~ThreadSafeQueue() = default; + + bool empty() + { + os::memory::LockHolder lock(mutex_); + return queue_.empty(); + } + + int size() + { + os::memory::LockHolder lock(mutex_); + return queue_.size(); + } + + void enqueue(T &t) + { + os::memory::LockHolder lock(mutex_); + queue_.push(t); + } + + bool dequeue(T &t) + { + os::memory::LockHolder lock(mutex_); + + if (queue_.empty()) { + return false; + } + t = std::move(queue_.front()); + queue_.pop(); + return true; + } + +private: + NO_COPY_SEMANTIC(ThreadSafeQueue); + NO_MOVE_SEMANTIC(ThreadSafeQueue); + + std::queue queue_; + os::memory::Mutex mutex_; +}; +} // namespace panda::ecmascript + +#endif // PANDA_ECMASCRIPT_THREAD_THREAD_SAFE_QUEUE_H diff --git a/ecmascript/tooling/BUILD.gn b/ecmascript/tooling/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..bb909be223e0e7440fec94b8aabd28012611a85b --- /dev/null +++ b/ecmascript/tooling/BUILD.gn @@ -0,0 +1,74 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//build/ohos.gni") + +config("ark_ecma_debugger_config") { + include_dirs = [ "//ark/js_runtime/ecmascript/tooling" ] +} + +ohos_static_library("libark_ecma_debugger_static") { + sources = [ + "agent/debugger_impl.cpp", + "agent/js_backend.cpp", + "agent/js_pt_hooks.cpp", + "agent/runtime_impl.cpp", + "base/pt_events.cpp", + "base/pt_params.cpp", + "base/pt_returns.cpp", + "base/pt_script.cpp", + "base/pt_types.cpp", + "debugger_service.cpp", + "dispatcher.cpp", + "protocol_handler.cpp", + "pt_js_extractor.cpp", + ] + + configs = [ + ":ark_ecma_debugger_config", + "//ark/js_runtime:ark_jsruntime_config", + "//ark/js_runtime:ark_jsruntime_public_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + "$ark_root/libpandafile:libarkfile", + ] + + output_extension = "a" + subsystem_name = "ark" +} + +ohos_shared_library("libark_ecma_debugger") { + deps = [ + ":libark_ecma_debugger_static", + "//ark/js_runtime:libark_jsruntime", + ] + + cflags_cc = [ "-fvisibility=hidden" ] + + if (!is_standard_system) { + deps += [ "$ark_root/runtime:libarkruntime" ] + } + + install_enable = true + part_name = "ark_js_runtime" + + output_extension = "so" + if (!is_standard_system) { + relative_install_dir = "ark" + } + subsystem_name = "ark" +} diff --git a/ecmascript/tooling/agent/debugger_impl.cpp b/ecmascript/tooling/agent/debugger_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a032b798b3dd47d3c1ccd141f427d74aed596f2a --- /dev/null +++ b/ecmascript/tooling/agent/debugger_impl.cpp @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/agent/debugger_impl.h" + +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "ecmascript/tooling/dispatcher.h" +#include "ecmascript/tooling/front_end.h" +#include "libpandabase/utils/logger.h" + +namespace panda::tooling::ecmascript { +DebuggerImpl::DispatcherImpl::DispatcherImpl(FrontEnd *frontend, std::unique_ptr debugger) + : DispatcherBase(frontend), debugger_(std::move(debugger)) +{ + dispatcherTable_["enable"] = &DebuggerImpl::DispatcherImpl::Enable; + dispatcherTable_["evaluateOnCallFrame"] = &DebuggerImpl::DispatcherImpl::EvaluateOnCallFrame; + dispatcherTable_["getPossibleBreakpoints"] = &DebuggerImpl::DispatcherImpl::GetPossibleBreakpoints; + dispatcherTable_["getScriptSource"] = &DebuggerImpl::DispatcherImpl::GetScriptSource; + dispatcherTable_["pause"] = &DebuggerImpl::DispatcherImpl::Pause; + dispatcherTable_["removeBreakpoint"] = &DebuggerImpl::DispatcherImpl::RemoveBreakpoint; + dispatcherTable_["resume"] = &DebuggerImpl::DispatcherImpl::Resume; + dispatcherTable_["setAsyncCallStackDepth"] = &DebuggerImpl::DispatcherImpl::SetAsyncCallStackDepth; + dispatcherTable_["setBreakpointByUrl"] = &DebuggerImpl::DispatcherImpl::SetBreakpointByUrl; + dispatcherTable_["setPauseOnExceptions"] = &DebuggerImpl::DispatcherImpl::SetPauseOnExceptions; + dispatcherTable_["stepInto"] = &DebuggerImpl::DispatcherImpl::StepInto; + dispatcherTable_["stepOut"] = &DebuggerImpl::DispatcherImpl::StepOut; + dispatcherTable_["stepOver"] = &DebuggerImpl::DispatcherImpl::StepOver; + dispatcherTable_["setBlackboxPatterns"] = &DebuggerImpl::DispatcherImpl::SetBlackboxPatterns; +} + +void DebuggerImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) +{ + CString method = request.GetMethod(); + LOG(DEBUG, DEBUGGER) << "dispatch [" << method << "] to DebuggerImpl"; + auto entry = dispatcherTable_.find(method); + if (entry != dispatcherTable_.end() && entry->second != nullptr) { + (this->*(entry->second))(request); + } else { + SendResponse(request, DispatchResponse::Fail("Unknown method: " + method), nullptr); + } +} + +void DebuggerImpl::DispatcherImpl::Enable(const DispatchRequest &request) +{ + std::unique_ptr params = EnableParams::Create(request.GetEcmaVM(), request.GetParams()); + + UniqueDebuggerId id; + DispatchResponse response = debugger_->Enable(std::move(params), &id); + + std::unique_ptr result = std::make_unique(id); + SendResponse(request, response, std::move(result)); +} + +void DebuggerImpl::DispatcherImpl::EvaluateOnCallFrame(const DispatchRequest &request) +{ + std::unique_ptr params = + EvaluateOnCallFrameParams::Create(request.GetEcmaVM(), request.GetParams()); + std::unique_ptr result1 = std::make_unique(); + DispatchResponse response = debugger_->EvaluateOnCallFrame(std::move(params), &result1); + + std::unique_ptr result = + std::make_unique(std::move(result1)); + SendResponse(request, response, std::move(result)); +} + +void DebuggerImpl::DispatcherImpl::GetPossibleBreakpoints(const DispatchRequest &request) +{ + std::unique_ptr params = + GetPossibleBreakpointsParams::Create(request.GetEcmaVM(), request.GetParams()); + CVector> locations; + DispatchResponse response = debugger_->GetPossibleBreakpoints(std::move(params), &locations); + std::unique_ptr points = + std::make_unique(std::move(locations)); + SendResponse(request, response, std::move(points)); +} + +void DebuggerImpl::DispatcherImpl::GetScriptSource(const DispatchRequest &request) +{ + std::unique_ptr params = + GetScriptSourceParams::Create(request.GetEcmaVM(), request.GetParams()); + CString source; + DispatchResponse response = debugger_->GetScriptSource(std::move(params), &source); + std::unique_ptr result = std::make_unique(std::move(source)); + SendResponse(request, response, std::move(result)); +} + +void DebuggerImpl::DispatcherImpl::Pause(const DispatchRequest &request) +{ + DispatchResponse response = debugger_->Pause(); + std::unique_ptr result = std::make_unique(); + SendResponse(request, response, std::move(result)); +} + +void DebuggerImpl::DispatcherImpl::RemoveBreakpoint(const DispatchRequest &request) +{ + std::unique_ptr params = + RemoveBreakpointParams::Create(request.GetEcmaVM(), request.GetParams()); + DispatchResponse response = debugger_->RemoveBreakpoint(std::move(params)); + std::unique_ptr result = std::make_unique(); + SendResponse(request, response, std::move(result)); +} + +void DebuggerImpl::DispatcherImpl::Resume(const DispatchRequest &request) +{ + std::unique_ptr params = ResumeParams::Create(request.GetEcmaVM(), request.GetParams()); + DispatchResponse response = debugger_->Resume(std::move(params)); + std::unique_ptr result = std::make_unique(); + SendResponse(request, response, std::move(result)); +} + +void DebuggerImpl::DispatcherImpl::SetAsyncCallStackDepth(const DispatchRequest &request) +{ + DispatchResponse response = debugger_->SetAsyncCallStackDepth(); + std::unique_ptr result = std::make_unique(); + SendResponse(request, response, std::move(result)); +} + +void DebuggerImpl::DispatcherImpl::SetBreakpointByUrl(const DispatchRequest &request) +{ + std::unique_ptr params = + SetBreakpointByUrlParams::Create(request.GetEcmaVM(), request.GetParams()); + + CString out_id; + CVector> outLocations; + DispatchResponse response = debugger_->SetBreakpointByUrl(std::move(params), &out_id, &outLocations); + std::unique_ptr result = + std::make_unique(out_id, std::move(outLocations)); + SendResponse(request, response, std::move(result)); +} + +void DebuggerImpl::DispatcherImpl::SetPauseOnExceptions(const DispatchRequest &request) +{ + std::unique_ptr params = + SetPauseOnExceptionsParams::Create(request.GetEcmaVM(), request.GetParams()); + + DispatchResponse response = debugger_->SetPauseOnExceptions(std::move(params)); + std::unique_ptr result = std::make_unique(); + SendResponse(request, response, std::move(result)); +} + +void DebuggerImpl::DispatcherImpl::StepInto(const DispatchRequest &request) +{ + std::unique_ptr params = StepIntoParams::Create(request.GetEcmaVM(), request.GetParams()); + DispatchResponse response = debugger_->StepInto(std::move(params)); + std::unique_ptr result = std::make_unique(); + SendResponse(request, response, std::move(result)); +} + +void DebuggerImpl::DispatcherImpl::StepOut(const DispatchRequest &request) +{ + DispatchResponse response = debugger_->StepOut(); + std::unique_ptr result = std::make_unique(); + SendResponse(request, response, std::move(result)); +} + +void DebuggerImpl::DispatcherImpl::StepOver(const DispatchRequest &request) +{ + std::unique_ptr params = StepOverParams::Create(request.GetEcmaVM(), request.GetParams()); + DispatchResponse response = debugger_->StepOver(std::move(params)); + std::unique_ptr result = std::make_unique(); + SendResponse(request, response, std::move(result)); +} + +void DebuggerImpl::DispatcherImpl::SetBlackboxPatterns(const DispatchRequest &request) +{ + DispatchResponse response = debugger_->SetBlackboxPatterns(); + std::unique_ptr result = std::make_unique(); + SendResponse(request, response, std::move(result)); +} + +DispatchResponse DebuggerImpl::Enable([[maybe_unused]] std::unique_ptr params, UniqueDebuggerId *id) +{ + ASSERT(id != nullptr); + *id = "0"; + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::EvaluateOnCallFrame(std::unique_ptr params, + std::unique_ptr *result) +{ + CString callFrameId = params->GetCallFrameId(); + CString expression = params->GetExpression(); + return DispatchResponse::Create(backend_->EvaluateValue(callFrameId, expression, result)); +} + +DispatchResponse DebuggerImpl::GetPossibleBreakpoints(std::unique_ptr params, + CVector> *locations) +{ + Location *locationStart = params->GetStart(); + Location *locationEnd = params->GetEnd(); + + return DispatchResponse::Create(backend_->GetPossibleBreakpoints(locationStart, locationEnd, locations)); +} + +DispatchResponse DebuggerImpl::GetScriptSource(std::unique_ptr params, CString *source) +{ + if (!backend_->MatchScripts( + [source](PtScript *script) -> bool { + *source = script->GetScriptSource(); + return true; + }, + params->GetScriptId(), ScriptMatchType::SCRIPT_ID)) { + *source = ""; + return DispatchResponse::Fail("unknown script id: " + params->GetScriptId()); + } + + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::Pause() +{ + return DispatchResponse::Create(backend_->Pause()); +} + +DispatchResponse DebuggerImpl::RemoveBreakpoint(std::unique_ptr params) +{ + CString id = params->GetBreakpointId(); + LOG(INFO, DEBUGGER) << "RemoveBreakpoint: " << id; + BreakpointDetails metaData{}; + if (!BreakpointDetails::ParseBreakpointId(id, &metaData)) { + return DispatchResponse::Fail("Parse breakpoint id failed"); + } + return DispatchResponse::Create(backend_->RemoveBreakpoint(metaData)); +} + +DispatchResponse DebuggerImpl::Resume([[maybe_unused]] std::unique_ptr params) +{ + return DispatchResponse::Create(backend_->Resume()); +} + +DispatchResponse DebuggerImpl::SetAsyncCallStackDepth() +{ + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::SetBreakpointByUrl(std::unique_ptr params, + CString *out_id, + CVector> *outLocations) +{ + return DispatchResponse::Create( + backend_->SetBreakpointByUrl(params->GetUrl(), params->GetLine(), params->GetColumn(), out_id, outLocations)); +} + +DispatchResponse DebuggerImpl::SetPauseOnExceptions(std::unique_ptr params) +{ + PauseOnExceptionsState state = params->GetState(); + if (state == PauseOnExceptionsState::UNCAUGHT) { + backend_->SetPauseOnException(false); + } else { + backend_->SetPauseOnException(true); + } + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::StepInto([[maybe_unused]] std::unique_ptr params) +{ + return DispatchResponse::Create(backend_->StepInto()); +} + +DispatchResponse DebuggerImpl::StepOut() +{ + return DispatchResponse::Create(backend_->StepOut()); +} + +DispatchResponse DebuggerImpl::StepOver([[maybe_unused]] std::unique_ptr params) +{ + return DispatchResponse::Create(backend_->StepOver()); +} + +DispatchResponse DebuggerImpl::SetBlackboxPatterns() +{ + return DispatchResponse::Ok(); +} +} // namespace panda::tooling::ecmascript diff --git a/ecmascript/tooling/agent/debugger_impl.h b/ecmascript/tooling/agent/debugger_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..946e6c974be59cdb6166db414a1df4b97577cf5f --- /dev/null +++ b/ecmascript/tooling/agent/debugger_impl.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_DEBUGGER_IMPL_H +#define PANDA_TOOLING_ECMASCRIPT_DEBUGGER_IMPL_H + +#include "libpandabase/macros.h" +#include "ecmascript/tooling/agent/js_backend.h" +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/dispatcher.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::CString; + +class DebuggerImpl final { +public: + explicit DebuggerImpl(std::unique_ptr backend) : backend_(std::move(backend)) {} + ~DebuggerImpl() = default; + + DispatchResponse Enable(std::unique_ptr params, UniqueDebuggerId *id); + DispatchResponse EvaluateOnCallFrame(std::unique_ptr params, + std::unique_ptr *result); + DispatchResponse GetPossibleBreakpoints(std::unique_ptr params, + CVector> *outLocations); + DispatchResponse GetScriptSource(std::unique_ptr params, CString *source); + DispatchResponse Pause(); + DispatchResponse RemoveBreakpoint(std::unique_ptr params); + DispatchResponse Resume(std::unique_ptr params); + DispatchResponse SetAsyncCallStackDepth(); + DispatchResponse SetBreakpointByUrl(std::unique_ptr params, CString *out_id, + CVector> *outLocations); + DispatchResponse SetPauseOnExceptions(std::unique_ptr params); + DispatchResponse StepInto(std::unique_ptr params); + DispatchResponse StepOut(); + DispatchResponse StepOver(std::unique_ptr params); + DispatchResponse SetBlackboxPatterns(); + + class DispatcherImpl final : public DispatcherBase { + public: + DispatcherImpl(FrontEnd *frontend, std::unique_ptr debugger); + ~DispatcherImpl() override = default; + void Dispatch(const DispatchRequest &request) override; + void Enable(const DispatchRequest &request); + void EvaluateOnCallFrame(const DispatchRequest &request); + void GetPossibleBreakpoints(const DispatchRequest &request); + void GetScriptSource(const DispatchRequest &request); + void Pause(const DispatchRequest &request); + void RemoveBreakpoint(const DispatchRequest &request); + void Resume(const DispatchRequest &request); + void SetAsyncCallStackDepth(const DispatchRequest &request); + void SetBreakpointByUrl(const DispatchRequest &request); + void SetPauseOnExceptions(const DispatchRequest &request); + void StepInto(const DispatchRequest &request); + void StepOut(const DispatchRequest &request); + void StepOver(const DispatchRequest &request); + void SetBlackboxPatterns(const DispatchRequest &request); + + private: + NO_COPY_SEMANTIC(DispatcherImpl); + NO_MOVE_SEMANTIC(DispatcherImpl); + + using AgentHandler = void (DebuggerImpl::DispatcherImpl::*)(const DispatchRequest &request); + CMap dispatcherTable_ {}; + std::unique_ptr debugger_ {}; + }; + +private: + NO_COPY_SEMANTIC(DebuggerImpl); + NO_MOVE_SEMANTIC(DebuggerImpl); + + std::unique_ptr backend_ {nullptr}; +}; +} // namespace panda::tooling::ecmascript +#endif \ No newline at end of file diff --git a/ecmascript/tooling/agent/js_backend.cpp b/ecmascript/tooling/agent/js_backend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e165c32b872bff0885c7a1c1107aae0e2241fb12 --- /dev/null +++ b/ecmascript/tooling/agent/js_backend.cpp @@ -0,0 +1,747 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_backend.h" +#include +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/front_end.h" +#include "ecmascript/tooling/protocol_handler.h" +#include "libpandafile/class_data_accessor-inl.h" + +namespace panda::tooling::ecmascript { +using ObjectType = RemoteObject::TypeName; +using ObjectSubType = RemoteObject::SubTypeName; +using ObjectClassName = RemoteObject::ClassName; + +const std::string DATA_APP_PATH = "/data/app/"; + +JSBackend::JSBackend(FrontEnd *frontend) : frontend_(frontend) +{ + ecmaVm_ = static_cast(frontend)->GetEcmaVM(); + hooks_ = std::make_unique(this); + + debugger_ = DebuggerApi::CreateJSDebugger(Runtime::GetCurrent(), ecmaVm_); + DebuggerApi::RegisterHooks(debugger_, hooks_.get()); +} + +JSBackend::JSBackend(const EcmaVM *vm) : ecmaVm_(vm) +{ + // For testcases + debugger_ = DebuggerApi::CreateJSDebugger(Runtime::GetCurrent(), ecmaVm_); +} + +JSBackend::~JSBackend() +{ + DebuggerApi::DestroyJSDebugger(debugger_); +} + +void JSBackend::ProcessCommand() +{ + frontend_->ProcessCommand(ecmaVm_); +} + +void JSBackend::WaitForDebugger() +{ + frontend_->WaitForDebugger(ecmaVm_); +} + +void JSBackend::NotifyPaused(std::optional location, PauseReason reason) +{ + if (!pauseOnException_ && reason == EXCEPTION) { + return; + } + CVector hitBreakpoints; + if (location.has_value()) { + BreakpointDetails detail; + PtJSExtractor *extractor = nullptr; + if (!MatchScripts([this, &extractor, &detail](PtScript *script) -> bool { + detail.url_ = script->GetUrl(); + extractor = GetExtractor(detail.url_); + return true; + }, + location.value().GetPandaFile(), + ScriptMatchType::FILE_NAME) || + extractor == nullptr || + !extractor->MatchWithOffset([&detail](int32_t line) -> bool { + detail.line_ = line; + return true; + }, + location.value().GetMethodId(), + location.value().GetBytecodeOffset())) { + LOG(ERROR, DEBUGGER) << "NotifyPaused: unknown " << location.value().GetPandaFile(); + return; + } + detail.column_ = 0; + hitBreakpoints.emplace_back(BreakpointDetails::ToString(detail)); + } + + // Notify paused event + CVector> callFrames; + if (!GenerateCallFrames(&callFrames)) { + LOG(ERROR, DEBUGGER) << "NotifyPaused: GenerateCallFrames failed"; + return; + } + std::unique_ptr paused = std::make_unique(); + paused->SetCallFrames(std::move(callFrames)).SetReason(reason).SetHitBreakpoints(std::move(hitBreakpoints)); + frontend_->SendNotification(ecmaVm_, std::move(paused)); + + // Waiting for Debugger + frontend_->WaitForDebugger(ecmaVm_); +} + +void JSBackend::NotifyResume() +{ + frontend_->RunIfWaitingForDebugger(); + // Notify resumed event + frontend_->SendNotification(ecmaVm_, std::make_unique()); +} + +bool JSBackend::NotifyScriptParsed(int32_t scriptId, const CString &fileName) +{ + if (MatchScripts([]([[maybe_unused]] PtScript *script) -> bool { return true; }, + fileName, + ScriptMatchType::FILE_NAME)) { + LOG(WARNING, DEBUGGER) << "NotifyScriptParsed: already loaded: " << fileName; + return false; + } + const panda_file::File *pfs = DebuggerApi::FindPandaFile(ecmaVm_, fileName); + if (pfs == nullptr) { + LOG(WARNING, DEBUGGER) << "NotifyScriptParsed: unknown file: " << fileName; + return false; + } + + auto classes = pfs->GetClasses(); + ASSERT(classes.Size() > 0); + size_t index = 0; + for (; index < classes.Size(); index++) { + if (!(*pfs).IsExternal(panda_file::File::EntityId(classes[index]))) { + break; + } + } + panda_file::ClassDataAccessor cda(*pfs, panda_file::File::EntityId(classes[index])); + auto lang = cda.GetSourceLang(); + if (lang.value_or(panda_file::SourceLang::PANDA_ASSEMBLY) != panda_file::SourceLang::ECMASCRIPT) { + LOG(ERROR, DEBUGGER) << "NotifyScriptParsed: Unsupport file: " << fileName; + return false; + } + + CString url; + CString source; + PtJSExtractor *extractor = GenerateExtractor(pfs); + if (extractor == nullptr) { + LOG(ERROR, DEBUGGER) << "NotifyScriptParsed: Unsupport file: " << fileName; + return false; + } + + const uint32_t MIN_SOURCE_CODE_LENGTH = 5; // maybe return 'ANDA' when source code is empty + for (const auto &method : extractor->GetMethodIdList()) { + source = CString(extractor->GetSourceCode(method)); + // only main function has source code + if (source.size() >= MIN_SOURCE_CODE_LENGTH) { + url = CString(extractor->GetSourceFile(method)); + break; + } + } + if (url.empty()) { + LOG(ERROR, DEBUGGER) << "NotifyScriptParsed: invalid file: " << fileName; + return false; + } + // Notify script parsed event + std::unique_ptr script = std::make_unique(scriptId, fileName, url, source); + + std::unique_ptr scriptParsed = std::make_unique(); + scriptParsed->SetScriptId(script->GetScriptId()) + .SetUrl(script->GetUrl()) + .SetStartLine(0) + .SetStartColumn(0) + .SetEndLine(script->GetEndLine()) + .SetEndColumn(0) + .SetExecutionContextId(0) + .SetHash(script->GetHash()); + if (frontend_ != nullptr) { + frontend_->SendNotification(ecmaVm_, std::move(scriptParsed)); + } + + // Store parsed script in map + scripts_[script->GetScriptId()] = std::move(script); + return true; +} + +bool JSBackend::StepComplete(const PtLocation &location) +{ + PtJSExtractor *extractor = nullptr; + if (MatchScripts([this, &extractor](PtScript *script) -> bool { + extractor = GetExtractor(script->GetUrl()); + return true; + }, + location.GetPandaFile(), + ScriptMatchType::FILE_NAME) && + extractor != nullptr && + extractor->MatchWithOffset([](int32_t line) -> bool { return line == SPECIAL_LINE_MARK; }, + location.GetMethodId(), + location.GetBytecodeOffset())) { + LOG(INFO, DEBUGGER) << "StepComplete: skip -1"; + return false; + } + + if (pauseOnNextByteCode_ || + (singleStepper_ != nullptr && singleStepper_->StepComplete(location.GetBytecodeOffset()))) { + LOG(INFO, DEBUGGER) << "StepComplete: pause on current byte_code"; + pauseOnNextByteCode_ = false; + return true; + } + + return false; +} + +std::optional JSBackend::GetPossibleBreakpoints( + Location *start, [[maybe_unused]] Location *end, CVector> *locations) +{ + PtJSExtractor *extractor = nullptr; + if (!MatchScripts([this, &extractor](PtScript *script) -> bool { + extractor = GetExtractor(script->GetUrl()); + return true; + }, + start->GetScriptId(), + ScriptMatchType::SCRIPT_ID) || extractor == nullptr) { + return Error(Error::Type::INVALID_BREAKPOINT, "extractor not found"); + } + + int32_t line = start->GetLine(); + if (extractor->MatchWithLine([](File::EntityId, uint32_t) -> bool { return true; }, line)) { + std::unique_ptr location = std::make_unique(); + location->SetScriptId(start->GetScriptId()).SetLine(line).SetColumn(0); + locations->emplace_back(std::move(location)); + } + + return {}; +} + +std::optional JSBackend::SetBreakpointByUrl(const CString &url, int32_t lineNumber, + [[maybe_unused]] int32_t columnNumber, CString *out_id, CVector> *outLocations) +{ + PtJSExtractor *extractor = GetExtractor(url); + if (extractor == nullptr) { + LOG(ERROR, DEBUGGER) << "SetBreakpointByUrl: extractor is null"; + return Error(Error::Type::METHOD_NOT_FOUND, "Extractor not found"); + } + + CString scriptId; + CString fileName; + if (!MatchScripts([&scriptId, &fileName](PtScript *script) -> bool { + scriptId = script->GetScriptId(); + fileName = script->GetFileName(); + return true; + }, + url, + ScriptMatchType::URL)) { + LOG(ERROR, DEBUGGER) << "SetBreakpointByUrl: Unknown url: " << url; + return Error(Error::Type::INVALID_BREAKPOINT, "Url not found"); + } + + std::optional ret = std::nullopt; + if (!extractor->MatchWithLine([this, fileName, &ret](File::EntityId id, uint32_t offset) -> bool { + PtLocation location {fileName.c_str(), id, offset}; + ret = DebuggerApi::SetBreakpoint(debugger_, location); + return true; + }, + lineNumber)) { + LOG(ERROR, DEBUGGER) << "failed to set breakpoint line number: " << lineNumber; + return Error(Error::Type::INVALID_BREAKPOINT, "Breakpoint not found"); + } + + if (!ret.has_value()) { + BreakpointDetails metaData{lineNumber, 0, url}; + *out_id = BreakpointDetails::ToString(metaData); + *outLocations = CVector>(); + std::unique_ptr location = std::make_unique(); + location->SetScriptId(scriptId).SetLine(lineNumber).SetColumn(0); + outLocations->emplace_back(std::move(location)); + } + + return ret; +} + +std::optional JSBackend::RemoveBreakpoint(const BreakpointDetails &metaData) +{ + PtJSExtractor *extractor = GetExtractor(metaData.url_); + if (extractor == nullptr) { + LOG(ERROR, DEBUGGER) << "RemoveBreakpoint: extractor is null"; + return Error(Error::Type::METHOD_NOT_FOUND, "Extractor not found"); + } + + CString fileName; + if (!MatchScripts([&fileName](PtScript *script) -> bool { + fileName = script->GetFileName(); + return true; + }, + metaData.url_, + ScriptMatchType::URL)) { + LOG(ERROR, DEBUGGER) << "RemoveBreakpoint: Unknown url: " << metaData.url_; + return Error(Error::Type::INVALID_BREAKPOINT, "Url not found"); + } + + std::optional ret = std::nullopt; + if (!extractor->MatchWithLine([this, fileName, &ret](File::EntityId id, uint32_t offset) -> bool { + PtLocation location {fileName.c_str(), id, offset}; + ret = DebuggerApi::RemoveBreakpoint(debugger_, location); + return true; + }, + metaData.line_)) { + LOG(ERROR, DEBUGGER) << "failed to set breakpoint line number: " << metaData.line_; + return Error(Error::Type::INVALID_BREAKPOINT, "Breakpoint not found"); + } + + LOG(INFO, DEBUGGER) << "remove breakpoint line number:" << metaData.line_; + return ret; +} + +std::optional JSBackend::Pause() +{ + pauseOnNextByteCode_ = true; + return {}; +} + +std::optional JSBackend::Resume() +{ + singleStepper_.reset(); + + NotifyResume(); + return {}; +} + +std::optional JSBackend::StepInto() +{ + JSMethod *method = DebuggerApi::GetMethod(ecmaVm_); + PtJSExtractor *extractor = GetExtractor(method->GetPandaFile()); + if (extractor == nullptr) { + LOG(ERROR, DEBUGGER) << "StepInto: extractor is null"; + return Error(Error::Type::METHOD_NOT_FOUND, "Extractor not found"); + } + + singleStepper_ = extractor->GetStepIntoStepper(ecmaVm_); + + NotifyResume(); + return {}; +} + +std::optional JSBackend::StepOver() +{ + JSMethod *method = DebuggerApi::GetMethod(ecmaVm_); + PtJSExtractor *extractor = GetExtractor(method->GetPandaFile()); + if (extractor == nullptr) { + LOG(ERROR, DEBUGGER) << "StepOver: extractor is null"; + return Error(Error::Type::METHOD_NOT_FOUND, "Extractor not found"); + } + + singleStepper_ = extractor->GetStepOverStepper(ecmaVm_); + + NotifyResume(); + return {}; +} + +std::optional JSBackend::StepOut() +{ + JSMethod *method = DebuggerApi::GetMethod(ecmaVm_); + PtJSExtractor *extractor = GetExtractor(method->GetPandaFile()); + if (extractor == nullptr) { + LOG(ERROR, DEBUGGER) << "StepOut: extractor is null"; + return Error(Error::Type::METHOD_NOT_FOUND, "Extractor not found"); + } + + singleStepper_ = extractor->GetStepOutStepper(ecmaVm_); + + NotifyResume(); + return {}; +} + +std::optional JSBackend::EvaluateValue( + const CString &callFrameId, const CString &expression, std::unique_ptr *result) +{ + JSMethod *method = DebuggerApi::GetMethod(ecmaVm_); + if (method->IsNative()) { + LOG(ERROR, DEBUGGER) << "EvaluateValue: Native Frame not support"; + *result = RemoteObject::FromTagged( + ecmaVm_, Exception::EvalError(ecmaVm_, StringRef::NewFromUtf8(ecmaVm_, "Runtime internal error"))); + return Error(Error::Type::METHOD_NOT_FOUND, "Native Frame not support"); + } + DebugInfoExtractor *extractor = GetExtractor(method->GetPandaFile()); + if (extractor == nullptr) { + LOG(ERROR, DEBUGGER) << "EvaluateValue: extractor is null"; + *result = RemoteObject::FromTagged( + ecmaVm_, Exception::EvalError(ecmaVm_, StringRef::NewFromUtf8(ecmaVm_, "Runtime internal error"))); + return Error(Error::Type::METHOD_NOT_FOUND, "Extractor not found"); + } + CString varName = expression; + CString varValue; + CString::size_type indexEqual = expression.find_first_of('=', 0); + if (indexEqual != CString::npos) { + varName = Trim(expression.substr(0, indexEqual)); + varValue = Trim(expression.substr(indexEqual + 1, expression.length())); + } + int32_t regIndex = -1; + auto varInfos = extractor->GetLocalVariableTable(method->GetFileId()); + for (const auto &varInfo : varInfos) { + if (varInfo.name == std::string(varName)) { + regIndex = varInfo.reg_number; + } + } + if (regIndex == -1) { + *result = RemoteObject::FromTagged( + ecmaVm_, Exception::EvalError(ecmaVm_, StringRef::NewFromUtf8(ecmaVm_, "Unknow input params"))); + return Error(Error::Type::METHOD_NOT_FOUND, "Unsupport expression"); + } + if (varValue.empty()) { + return GetValue(regIndex, result); + } + if (callFrameId != "0") { + *result = RemoteObject::FromTagged(ecmaVm_, + Exception::EvalError(ecmaVm_, StringRef::NewFromUtf8(ecmaVm_, "Only allow set value in current frame"))); + return Error(Error::Type::METHOD_NOT_FOUND, "Unsupport parent frame set value"); + } + return SetValue(regIndex, result, varValue); +} + +CString JSBackend::Trim(const CString &str) +{ + CString ret = str; + // If ret has only ' ', remove all charactors. + ret.erase(ret.find_last_not_of(' ') + 1); + // If ret has only ' ', remove all charactors. + ret.erase(0, ret.find_first_not_of(' ')); + return ret; +} + +PtJSExtractor *JSBackend::GenerateExtractor(const panda_file::File *file) +{ + if (file->GetFilename().substr(0, DATA_APP_PATH.length()) != DATA_APP_PATH) { + return nullptr; + } + auto extractor = std::make_unique(file); + PtJSExtractor *res = extractor.get(); + extractors_[file] = std::move(extractor); + return res; +} + +PtJSExtractor *JSBackend::GetExtractor(const panda_file::File *file) +{ + if (extractors_.find(file) == extractors_.end()) { + return nullptr; + } + + return extractors_[file].get(); +} + +PtJSExtractor *JSBackend::GetExtractor(const CString &url) +{ + for (const auto &iter : extractors_) { + auto methods = iter.second->GetMethodIdList(); + for (const auto &method : methods) { + auto sourceFile = iter.second->GetSourceFile(method); + if (sourceFile == url) { + return iter.second.get(); + } + } + } + return nullptr; +} + +bool JSBackend::GenerateCallFrames(CVector> *callFrames) +{ + int32_t callFrameId = 0; + return DebuggerApi::StackWalker( + ecmaVm_, [this, &callFrameId, &callFrames](const EcmaFrameHandler *frameHandler) -> StackState { + JSMethod *method = DebuggerApi::GetMethod(frameHandler); + if (method->IsNative()) { + LOG(INFO, DEBUGGER) << "GenerateCallFrames: Skip CFrame and Native method"; + return StackState::CONTINUE; + } + std::unique_ptr callFrame = std::make_unique(); + if (!GenerateCallFrame(callFrame.get(), frameHandler, callFrameId)) { + if (callFrameId == 0) { + return StackState::FAILED; + } + } + callFrames->emplace_back(std::move(callFrame)); + callFrameId++; + return StackState::CONTINUE; + }); + return true; +} + +bool JSBackend::GenerateCallFrame(CallFrame *callFrame, const EcmaFrameHandler *frameHandler, int32_t callFrameId) +{ + JSMethod *method = DebuggerApi::GetMethod(frameHandler); + auto *pf = method->GetPandaFile(); + PtJSExtractor *extractor = GetExtractor(pf); + if (extractor == nullptr) { + LOG(ERROR, DEBUGGER) << "GenerateCallFrame: extractor is null"; + return false; + } + + // location + std::unique_ptr location = std::make_unique(); + CString url = extractor->GetSourceFile(method->GetFileId()); + if (!MatchScripts([&location](PtScript *script) -> bool { + location->SetScriptId(script->GetScriptId()); + return true; + }, + url, + ScriptMatchType::URL)) { + LOG(ERROR, DEBUGGER) << "GenerateCallFrame: Unknown url: " << url; + return false; + } + if (!extractor->MatchWithOffset([&location](int32_t line) -> bool { + location->SetLine(line); + return true; + }, + method->GetFileId(), + DebuggerApi::GetBytecodeOffset(frameHandler))) { + LOG(ERROR, DEBUGGER) << "GenerateCallFrame: unknown offset: " << DebuggerApi::GetBytecodeOffset(frameHandler); + return false; + } + + // scopeChain & this + std::unique_ptr thisObj = std::make_unique(); + thisObj->SetType(ObjectType::Undefined); + + CVector> scopeChain; + scopeChain.emplace_back(GetLocalScopeChain(frameHandler, &thisObj)); + scopeChain.emplace_back(GetGlobalScopeChain()); + + // functionName + panda_file::MethodDataAccessor mda(*pf, method->GetFileId()); + CString functionName = CString(utf::Mutf8AsCString(pf->GetStringData( + panda_file::File::EntityId(mda.GetNumericalAnnotation(JSMethod::AnnotationField::FUNCTION_NAME))).data)); + + callFrame->SetCallFrameId(DebuggerApi::ToCString(callFrameId)) + .SetFunctionName(functionName) + .SetLocation(std::move(location)) + .SetUrl(url) + .SetScopeChain(std::move(scopeChain)) + .SetThis(std::move(thisObj)); + return true; +} + +std::unique_ptr JSBackend::GetLocalScopeChain( + const EcmaFrameHandler *frameHandler, std::unique_ptr *thisObj) +{ + auto localScope = std::make_unique(); + + std::unique_ptr local = std::make_unique(); + Local localObject(local->NewObject(ecmaVm_)); + local->SetType(ObjectType::Object) + .SetObjectId(curObjectId_) + .SetClassName(ObjectClassName::Object) + .SetDescription(RemoteObject::ObjectDescription); + propertiesPair_[curObjectId_++] = Global(ecmaVm_, localObject); + + PtJSExtractor *extractor = GetExtractor(DebuggerApi::GetMethod(frameHandler)->GetPandaFile()); + if (extractor == nullptr) { + LOG(ERROR, DEBUGGER) << "GetScopeChain: extractor is null"; + return localScope; + } + panda_file::File::EntityId methodId = DebuggerApi::GetMethod(frameHandler)->GetFileId(); + Local name = JSValueRef::Undefined(ecmaVm_); + Local value = JSValueRef::Undefined(ecmaVm_); + for (const auto &var : extractor->GetLocalVariableTable(methodId)) { + value = DebuggerApi::GetVRegValue(ecmaVm_, frameHandler, var.reg_number); + if (var.name == "this") { + *thisObj = RemoteObject::FromTagged(ecmaVm_, value); + if (value->IsObject() && !value->IsProxy()) { + (*thisObj)->SetObjectId(curObjectId_); + propertiesPair_[curObjectId_++] = Global(ecmaVm_, value); + } + } else { + name = StringRef::NewFromUtf8(ecmaVm_, var.name.c_str()); + PropertyAttribute descriptor(value, true, true, true); + localObject->DefineProperty(ecmaVm_, name, descriptor); + } + } + + const panda_file::LineNumberTable &lines = extractor->GetLineNumberTable(methodId); + std::unique_ptr startLoc = std::make_unique(); + std::unique_ptr endLoc = std::make_unique(); + if (MatchScripts([&startLoc, &endLoc, lines](PtScript *script) -> bool { + startLoc->SetScriptId(script->GetScriptId()) + .SetLine(static_cast(lines.front().line)) + .SetColumn(0); + endLoc->SetScriptId(script->GetScriptId()) + .SetLine(static_cast(lines.back().line)) + .SetColumn(0); + return true; + }, + extractor->GetSourceFile(methodId), + ScriptMatchType::URL)) { + localScope->SetType(Scope::Type::Local()) + .SetObject(std::move(local)) + .SetStartLocation(std::move(startLoc)) + .SetEndLocation(std::move(endLoc)); + } + + return localScope; +} + +std::unique_ptr JSBackend::GetGlobalScopeChain() +{ + auto globalScope = std::make_unique(); + + std::unique_ptr global = std::make_unique(); + global->SetType(ObjectType::Object) + .SetObjectId(curObjectId_) + .SetClassName(ObjectClassName::Global) + .SetDescription(RemoteObject::GlobalDescription); + globalScope->SetType(Scope::Type::Global()).SetObject(std::move(global)); + propertiesPair_[curObjectId_++] = Global(ecmaVm_, JSNApi::GetGlobalObject(ecmaVm_)); + return globalScope; +} + +void JSBackend::SetPauseOnException(bool flag) +{ + pauseOnException_ = flag; +} + +std::optional JSBackend::SetValue( + int32_t regIndex, std::unique_ptr *result, const CString &varValue) +{ + Local taggedValue; + if (varValue == "false") { + taggedValue = JSValueRef::False(ecmaVm_); + } else if (varValue == "true") { + taggedValue = JSValueRef::True(ecmaVm_); + } else if (varValue == "undefined") { + taggedValue = JSValueRef::Undefined(ecmaVm_); + } else if (varValue[0] == '\"' && varValue[varValue.length() - 1] == '\"') { + // 2 : 2 means length + taggedValue = StringRef::NewFromUtf8(ecmaVm_, varValue.substr(1, varValue.length() - 2).c_str()); + } else { + auto begin = reinterpret_cast((varValue.c_str())); + auto end = begin + varValue.length(); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + double d = DebuggerApi::StringToDouble(begin, end, 0); + if (std::isnan(d)) { + *result = RemoteObject::FromTagged( + ecmaVm_, Exception::EvalError(ecmaVm_, StringRef::NewFromUtf8(ecmaVm_, "Unsupport value"))); + return Error(Error::Type::METHOD_NOT_FOUND, "Unsupport value"); + } + taggedValue = NumberRef::New(ecmaVm_, d); + } + DebuggerApi::SetVRegValue(ecmaVm_, regIndex, taggedValue); + + *result = RemoteObject::FromTagged(ecmaVm_, taggedValue); + return {}; +} + +std::optional JSBackend::GetValue(int32_t regIndex, std::unique_ptr *result) +{ + Local vValue = DebuggerApi::GetVRegValue(ecmaVm_, regIndex); + *result = RemoteObject::FromTagged(ecmaVm_, vValue); + if (vValue->IsObject() && !vValue->IsProxy()) { + (*result)->SetObjectId(curObjectId_); + propertiesPair_[curObjectId_++] = Global(ecmaVm_, vValue); + } + return {}; +} + +void JSBackend::GetProtoOrProtoType(const Local &value, bool isOwn, bool isAccessorOnly, + CVector> *outPropertyDesc) +{ + if (!isAccessorOnly && isOwn && !value->IsProxy()) { + return; + } + // Get Function ProtoOrDynClass + if (value->IsConstructor()) { + Local prototype = Local(value)->GetFunctionPrototype(ecmaVm_); + std::unique_ptr protoObj = RemoteObject::FromTagged(ecmaVm_, prototype); + if (prototype->IsObject() && !prototype->IsProxy()) { + protoObj->SetObjectId(curObjectId_); + propertiesPair_[curObjectId_++] = Global(ecmaVm_, prototype); + } + std::unique_ptr debuggerProperty = std::make_unique(); + debuggerProperty->SetName("prototype") + .SetWritable(false) + .SetConfigurable(false) + .SetEnumerable(false) + .SetIsOwn(true) + .SetValue(std::move(protoObj)); + outPropertyDesc->emplace_back(std::move(debuggerProperty)); + } + // Get __proto__ + Local proto = Local(value)->GetPrototype(ecmaVm_); + std::unique_ptr protoObj = RemoteObject::FromTagged(ecmaVm_, proto); + if (proto->IsObject() && !proto->IsProxy()) { + protoObj->SetObjectId(curObjectId_); + propertiesPair_[curObjectId_++] = Global(ecmaVm_, proto); + } + std::unique_ptr debuggerProperty = std::make_unique(); + debuggerProperty->SetName("__proto__") + .SetWritable(true) + .SetConfigurable(true) + .SetEnumerable(false) + .SetIsOwn(true) + .SetValue(std::move(protoObj)); + outPropertyDesc->emplace_back(std::move(debuggerProperty)); +} + +void JSBackend::GetProperties(uint32_t objectId, bool isOwn, bool isAccessorOnly, + CVector> *outPropertyDesc) +{ + auto iter = propertiesPair_.find(objectId); + if (iter == propertiesPair_.end()) { + LOG(ERROR, DEBUGGER) << "JSBackend::GetProperties Unknown object id: " << objectId; + return; + } + Local value = Local(ecmaVm_, iter->second); + if (value.IsEmpty() || !value->IsObject()) { + LOG(ERROR, DEBUGGER) << "JSBackend::GetProperties should a js object"; + return; + } + Local keys = Local(value)->GetOwnPropertyNames(ecmaVm_); + array_size_t length = keys->Length(ecmaVm_); + Local name = JSValueRef::Undefined(ecmaVm_); + for (array_size_t i = 0; i < length; ++i) { + name = keys->Get(ecmaVm_, i); + PropertyAttribute jsProperty = PropertyAttribute::Default(); + if (!Local(value)->GetOwnProperty(ecmaVm_, name, jsProperty)) { + continue; + } + std::unique_ptr debuggerProperty = + PropertyDescriptor::FromProperty(ecmaVm_, name, jsProperty); + if (isAccessorOnly && !jsProperty.HasGetter() && !jsProperty.HasSetter()) { + continue; + } + if (jsProperty.HasGetter()) { + debuggerProperty->GetGet()->SetObjectId(curObjectId_); + propertiesPair_[curObjectId_++] = Global(ecmaVm_, jsProperty.GetGetter(ecmaVm_)); + } + if (jsProperty.HasSetter()) { + debuggerProperty->GetSet()->SetObjectId(curObjectId_); + propertiesPair_[curObjectId_++] = Global(ecmaVm_, jsProperty.GetSetter(ecmaVm_)); + } + if (jsProperty.HasValue()) { + Local vValue = jsProperty.GetValue(ecmaVm_); + if (vValue->IsObject() && !vValue->IsProxy()) { + debuggerProperty->GetValue()->SetObjectId(curObjectId_); + propertiesPair_[curObjectId_++] = Global(ecmaVm_, vValue); + } + } + if (name->IsSymbol()) { + debuggerProperty->GetSymbol()->SetObjectId(curObjectId_); + propertiesPair_[curObjectId_++] = Global(ecmaVm_, name); + } + outPropertyDesc->emplace_back(std::move(debuggerProperty)); + } + GetProtoOrProtoType(value, isOwn, isAccessorOnly, outPropertyDesc); +} +} // namespace panda::tooling::ecmascript diff --git a/ecmascript/tooling/agent/js_backend.h b/ecmascript/tooling/agent/js_backend.h new file mode 100644 index 0000000000000000000000000000000000000000..34584a0ac37c9a4566fbd11f2ff001fb25e1086b --- /dev/null +++ b/ecmascript/tooling/agent/js_backend.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_JS_BACKEND_H +#define PANDA_TOOLING_ECMASCRIPT_JS_BACKEND_H + +#include "ecmascript/tooling/agent/js_pt_hooks.h" +#include "ecmascript/tooling/base/pt_types.h" +#include "ecmascript/tooling/dispatcher.h" +#include "ecmascript/tooling/pt_js_extractor.h" +#include "libpandabase/macros.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::CString; + +class JSBackend { +public: + explicit JSBackend(FrontEnd *frontend); + explicit JSBackend(const EcmaVM *vm); + ~JSBackend(); + + // add for hooks + void ProcessCommand(); + void WaitForDebugger(); + void NotifyPaused(std::optional location, PauseReason reason); + void NotifyResume(); + bool NotifyScriptParsed(int32_t scriptId, const CString &fileName); + bool StepComplete(const PtLocation &location); + + std::optional GetPossibleBreakpoints(Location *start, Location *end, + CVector> *locations); + JSDebugger *GetDebugger() const + { + return debugger_; + } + + std::optional SetBreakpointByUrl(const CString &url, int32_t lineNumber, int32_t columnNumber, + CString *outId, + CVector> *outLocations); + std::optional RemoveBreakpoint(const BreakpointDetails &metaData); + + std::optional Pause(); + std::optional Resume(); + std::optional StepInto(); + std::optional StepOver(); + std::optional StepOut(); + std::optional EvaluateValue(const CString &callFrameId, const CString &expression, + std::unique_ptr *result); + + /** + * @brief: match first script and callback + * + * @return: true means matched and callback execute success + */ + template + bool MatchScripts(const Callback &cb, const CString &matchStr, ScriptMatchType type) const + { + for (const auto &script : scripts_) { + CString value; + switch (type) { + case ScriptMatchType::SCRIPT_ID: { + value = script.second->GetScriptId(); + break; + } + case ScriptMatchType::URL: { + value = script.second->GetUrl(); + break; + } + case ScriptMatchType::FILE_NAME: { + value = script.second->GetFileName(); + break; + } + case ScriptMatchType::HASH: { + value = script.second->GetHash(); + break; + } + default: { + return false; + } + } + if (matchStr == value) { + return cb(script.second.get()); + } + } + return false; + } + + void SetPauseOnException(bool flag); + void GetProperties(uint32_t objectId, bool isOwn, bool isAccessorOnly, + CVector> *outPropertyDesc); + +private: + NO_MOVE_SEMANTIC(JSBackend); + NO_COPY_SEMANTIC(JSBackend); + CString Trim(const CString &str); + PtJSExtractor *GenerateExtractor(const panda_file::File *file); + PtJSExtractor *GetExtractor(const panda_file::File *file); + PtJSExtractor *GetExtractor(const CString &url); + bool GenerateCallFrames(CVector> *callFrames); + bool GenerateCallFrame(CallFrame *callFrame, const EcmaFrameHandler *frameHandler, int32_t frameId); + std::unique_ptr GetLocalScopeChain(const EcmaFrameHandler *frameHandler, + std::unique_ptr *thisObj); + std::unique_ptr GetGlobalScopeChain(); + std::optional SetValue(int32_t regIndex, std::unique_ptr *result, const CString &varValue); + std::optional GetValue(int32_t regIndex, std::unique_ptr *result); + void GetProtoOrProtoType(const Local &value, bool isOwn, bool isAccessorOnly, + CVector> *outPropertyDesc); + constexpr static int32_t SPECIAL_LINE_MARK = -1; + + FrontEnd *frontend_ {nullptr}; + const EcmaVM *ecmaVm_ {nullptr}; + std::unique_ptr hooks_ {nullptr}; + JSDebugger *debugger_ {nullptr}; + CMap> extractors_ {}; + CMap> scripts_ {}; + CMap> propertiesPair_ {}; + uint32_t curObjectId_ {0}; + bool pauseOnException_ {false}; + bool pauseOnNextByteCode_ {false}; + std::unique_ptr singleStepper_ {nullptr}; + + friend class JSPtHooks; +}; +} // namespace panda::tooling::ecmascript +#endif diff --git a/ecmascript/tooling/agent/js_pt_hooks.cpp b/ecmascript/tooling/agent/js_pt_hooks.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e5883167093ac92b699a837f6f5f769c508fef68 --- /dev/null +++ b/ecmascript/tooling/agent/js_pt_hooks.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/agent/js_pt_hooks.h" +#include "ecmascript/tooling/agent/js_backend.h" + +namespace panda::tooling::ecmascript { +void JSPtHooks::Breakpoint([[maybe_unused]] PtThread ecmaVm, const PtLocation &location) +{ + if (ecmaVm.GetId() != ManagedThread::NON_INITIALIZED_THREAD_ID) { + // Skip none-js ecmaVm + return; + } + LOG(DEBUG, DEBUGGER) << "JSPtHooks: Breakpoint => " << location.GetMethodId() << ": " + << location.GetBytecodeOffset(); + + [[maybe_unused]] LocalScope scope(backend_->ecmaVm_); + backend_->NotifyPaused(location, INSTRUMENTATION); +} + +void JSPtHooks::Paused(PauseReason reason) +{ + LOG(DEBUG, DEBUGGER) << "JSPtHooks: Paused"; + + [[maybe_unused]] LocalScope scope(backend_->ecmaVm_); + backend_->NotifyPaused({}, reason); +} + +void JSPtHooks::Exception([[maybe_unused]] PtThread ecmaVm, [[maybe_unused]] const PtLocation &location, + [[maybe_unused]] PtObject exceptionObject, [[maybe_unused]] const PtLocation &catchLocation) +{ + if (ecmaVm.GetId() != ManagedThread::NON_INITIALIZED_THREAD_ID) { + // Skip none-js ecmaVm + return; + } + LOG(DEBUG, DEBUGGER) << "JSPtHooks: Exception"; + + [[maybe_unused]] LocalScope scope(backend_->ecmaVm_); + Local exception = DebuggerApi::GetException(backend_->ecmaVm_); + DebuggerApi::ClearException(backend_->ecmaVm_); + + backend_->NotifyPaused({}, EXCEPTION); + + if (!exception->IsHole()) { + DebuggerApi::SetException(backend_->ecmaVm_, exception); + } +} + +void JSPtHooks::SingleStep([[maybe_unused]] PtThread ecmaVm, const PtLocation &location) +{ + if (ecmaVm.GetId() != ManagedThread::NON_INITIALIZED_THREAD_ID) { + // Skip none-js ecmaVm + return; + } + LOG(DEBUG, DEBUGGER) << "JSPtHooks: SingleStep => " << location.GetBytecodeOffset(); + + [[maybe_unused]] LocalScope scope(backend_->ecmaVm_); + if (firstTime_) { + firstTime_ = false; + + backend_->NotifyPaused({}, BREAK_ON_START); + return; + } + + // pause or step complete + if (backend_->StepComplete(location)) { + backend_->NotifyPaused({}, OTHER); + } else { + backend_->ProcessCommand(); + } +} + +void JSPtHooks::LoadModule(std::string_view pandaFileName) +{ + LOG(DEBUG, DEBUGGER) << "JSPtHooks: LoadModule: " << pandaFileName; + + [[maybe_unused]] LocalScope scope(backend_->ecmaVm_); + static bool firstTime = true; + if (firstTime) { + firstTime = false; + backend_->WaitForDebugger(); + } + static int32_t scriptId = 0; + + if (backend_->NotifyScriptParsed(scriptId++, DebuggerApi::ConvertToString(pandaFileName.data()))) { + firstTime_ = true; + } +} +} // namespace panda::tooling::ecmascript diff --git a/ecmascript/tooling/agent/js_pt_hooks.h b/ecmascript/tooling/agent/js_pt_hooks.h new file mode 100644 index 0000000000000000000000000000000000000000..d8d44ab362718d0ccfd522ff1d21ad3c53be167f --- /dev/null +++ b/ecmascript/tooling/agent/js_pt_hooks.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_JS_PT_HOOKS_H +#define PANDA_TOOLING_ECMASCRIPT_JS_PT_HOOKS_H + +#include "libpandabase/macros.h" +#include "ecmascript/tooling/pt_js_extractor.h" +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/base/pt_script.h" +#include "include/tooling/debug_interface.h" + +namespace panda::tooling::ecmascript { +class JSBackend; + +class JSPtHooks : public PtHooks { +public: + explicit JSPtHooks(JSBackend *backend) : backend_(backend) {} + ~JSPtHooks() override = default; + + void Breakpoint(PtThread ecmaVm, const PtLocation &location) override; + void LoadModule(std::string_view pandaFileName) override; + void Paused(PauseReason reason) override; + void Exception(PtThread ecmaVm, const PtLocation &location, PtObject exceptionObject, + const PtLocation &catchLocation) override; + void SingleStep(PtThread ecmaVm, const PtLocation &location) override; + + void ThreadStart(PtThread) override {} + void ThreadEnd(PtThread) override {} + void VmStart() override {} + void VmInitialization(PtThread) override {} + void VmDeath() override {} + void ClassLoad(PtThread, PtClass) override {} + void ClassPrepare(PtThread, PtClass) override {} + void MonitorWait(PtThread, PtObject, int64_t) override {} + void MonitorWaited(PtThread, PtObject, bool) override {} + void MonitorContendedEnter(PtThread, PtObject) override {} + void MonitorContendedEntered(PtThread, PtObject) override {} + void ExceptionCatch(PtThread, const PtLocation &, PtObject) override {} + void PropertyAccess(PtThread, const PtLocation &, PtObject, PtProperty) override {} + void PropertyModification(PtThread, const PtLocation &, PtObject, PtProperty, PtValue) override {} + void FramePop(PtThread, PtMethod, bool) override {} + void GarbageCollectionFinish() override {} + void GarbageCollectionStart() override {} + void ObjectAlloc(PtClass, PtObject, PtThread, size_t) override {} + void MethodEntry(PtThread, PtMethod) override {} + void MethodExit(PtThread, PtMethod, bool, PtValue) override {} + void ExceptionRevoked(ExceptionWrapper, ExceptionID) override {} + void ExecutionContextCreated(ExecutionContextWrapper) override {} + void ExecutionContextDestroyed(ExecutionContextWrapper) override {} + void ExecutionContextsCleared() override {} + void InspectRequested(PtObject, PtObject) override {} + +private: + NO_COPY_SEMANTIC(JSPtHooks); + NO_MOVE_SEMANTIC(JSPtHooks); + + JSBackend *backend_{nullptr}; + bool firstTime_ {true}; +}; +} // namespace panda::tooling::ecmascript +#endif \ No newline at end of file diff --git a/ecmascript/tooling/agent/runtime_impl.cpp b/ecmascript/tooling/agent/runtime_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..81779c53099575540e52f4d52d0442fba2195ebc --- /dev/null +++ b/ecmascript/tooling/agent/runtime_impl.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/agent/runtime_impl.h" + +#include "ecmascript/tooling/base/pt_returns.h" +#include "libpandabase/utils/logger.h" + +namespace panda::tooling::ecmascript { +RuntimeImpl::DispatcherImpl::DispatcherImpl(FrontEnd *frontend, std::unique_ptr runtime) + : DispatcherBase(frontend), runtime_(std::move(runtime)) +{ + dispatcherTable_["enable"] = &RuntimeImpl::DispatcherImpl::Enable; + dispatcherTable_["getProperties"] = &RuntimeImpl::DispatcherImpl::GetProperties; + dispatcherTable_["runIfWaitingForDebugger"] = &RuntimeImpl::DispatcherImpl::RunIfWaitingForDebugger; +} + +void RuntimeImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) +{ + CString method = request.GetMethod(); + LOG(DEBUG, DEBUGGER) << "dispatch [" << method << "] to RuntimeImpl"; + + auto entry = dispatcherTable_.find(method); + if (entry != dispatcherTable_.end()) { + (this->*(entry->second))(request); + } else { + LOG(ERROR, DEBUGGER) << "unknown method: " << method; + SendResponse(request, DispatchResponse::Fail("unknown method: " + method), nullptr); + } +} + +void RuntimeImpl::DispatcherImpl::Enable(const DispatchRequest &request) +{ + DispatchResponse response = runtime_->Enable(); + SendResponse(request, response, nullptr); +} + +void RuntimeImpl::DispatcherImpl::RunIfWaitingForDebugger(const DispatchRequest &request) +{ + DispatchResponse response = runtime_->RunIfWaitingForDebugger(); + SendResponse(request, response, nullptr); +} + +void RuntimeImpl::DispatcherImpl::GetProperties(const DispatchRequest &request) +{ + std::unique_ptr params = GetPropertiesParams::Create(request.GetEcmaVM(), request.GetParams()); + + CVector> outPropertyDesc; + std::optional>> outInternalDescs; + std::optional>> outPrivateProperties; + std::optional> outExceptionDetails; + DispatchResponse response = runtime_->GetProperties( + std::move(params), &outPropertyDesc, &outInternalDescs, &outPrivateProperties, &outExceptionDetails); + if (outExceptionDetails) { + LOG(WARNING, DEBUGGER) << "GetProperties thrown an exception"; + } + std::unique_ptr result = std::make_unique(std::move(outPropertyDesc), + std::move(outInternalDescs), + std::move(outPrivateProperties), + std::move(outExceptionDetails)); + SendResponse(request, response, std::move(result)); +} + +DispatchResponse RuntimeImpl::Enable() +{ + return DispatchResponse::Ok(); +} + +DispatchResponse RuntimeImpl::RunIfWaitingForDebugger() +{ + return DispatchResponse::Create(backend_->Resume()); +} + +DispatchResponse RuntimeImpl::GetProperties(std::unique_ptr params, + CVector> *outPropertyDesc, + [[maybe_unused]] std::optional>> *outInternalDescs, + [[maybe_unused]] std::optional>> *outPrivateProps, + [[maybe_unused]] std::optional> *outExceptionDetails) +{ + backend_->GetProperties(DebuggerApi::CStringToULL(params->GetObjectId()), + params->GetOwnProperties(), + params->GetAccessPropertiesOnly(), + outPropertyDesc); + return DispatchResponse::Ok(); +} +} // namespace panda::tooling::ecmascript diff --git a/ecmascript/tooling/agent/runtime_impl.h b/ecmascript/tooling/agent/runtime_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..6036f57d0b797430c580ed76c9a9169a1ad59de5 --- /dev/null +++ b/ecmascript/tooling/agent/runtime_impl.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_RUNTIME_IMPL_H +#define PANDA_TOOLING_ECMASCRIPT_RUNTIME_IMPL_H + +#include "libpandabase/macros.h" +#include "ecmascript/tooling/agent/js_backend.h" +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/dispatcher.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::CString; + +class RuntimeImpl final { +public: + explicit RuntimeImpl(JSBackend *backend) : backend_(backend) {} + ~RuntimeImpl() = default; + + DispatchResponse Enable(); + DispatchResponse RunIfWaitingForDebugger(); + DispatchResponse GetProperties( + std::unique_ptr params, + CVector> *outPropertyDesc, + std::optional>> *outInternalDescs, + std::optional>> *outPrivateProps, + std::optional> *outExceptionDetails); + + class DispatcherImpl final : public DispatcherBase { + public: + DispatcherImpl(FrontEnd *frontend, std::unique_ptr runtime); + ~DispatcherImpl() override = default; + + void Dispatch(const DispatchRequest &request) override; + void Enable(const DispatchRequest &request); + void RunIfWaitingForDebugger(const DispatchRequest &request); + void GetProperties(const DispatchRequest &request); + + private: + using AgentHandler = void (RuntimeImpl::DispatcherImpl::*)(const DispatchRequest &request); + CMap dispatcherTable_ {}; + std::unique_ptr runtime_ {}; + + NO_COPY_SEMANTIC(DispatcherImpl); + NO_MOVE_SEMANTIC(DispatcherImpl); + }; + +private: + NO_COPY_SEMANTIC(RuntimeImpl); + NO_MOVE_SEMANTIC(RuntimeImpl); + + JSBackend *backend_{nullptr}; +}; +} // namespace panda::tooling::ecmascript +#endif \ No newline at end of file diff --git a/ecmascript/tooling/base/pt_events.cpp b/ecmascript/tooling/base/pt_events.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1d8521dca0802ceda79165d8081548e03195808e --- /dev/null +++ b/ecmascript/tooling/base/pt_events.cpp @@ -0,0 +1,702 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/base/pt_events.h" + +namespace panda::tooling::ecmascript { +std::unique_ptr BreakpointResolved::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "BreakpointResolved::Create params is nullptr"; + return nullptr; + } + CString error; + auto breakpointResolved = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "breakpointId"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + breakpointResolved->breakpointId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'breakpointId' should a String;"; + } + } else { + error += "should contain 'breakpointId';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "location"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr location = Location::Create(ecmaVm, result); + if (location == nullptr) { + error += "'location' format error;"; + } else { + breakpointResolved->location_ = std::move(location); + } + } else { + error += "'exception' should a Object;"; + } + } else { + error += "should contain 'location';"; + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "BreakpointResolved::Create " << error; + return nullptr; + } + + return breakpointResolved; +} + +Local BreakpointResolved::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "breakpointId")), + Local(StringRef::NewFromUtf8(ecmaVm, breakpointId_.c_str()))); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "location")), + Local(location_->ToObject(ecmaVm))); + + Local object = NewObject(ecmaVm); + object->Set(ecmaVm, + StringRef::NewFromUtf8(ecmaVm, "method"), + Local(StringRef::NewFromUtf8(ecmaVm, GetName().c_str()))); + object->Set(ecmaVm, StringRef::NewFromUtf8(ecmaVm, "params"), Local(params)); + + return object; +} + +std::unique_ptr Paused::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "Paused::Create params is nullptr"; + return nullptr; + } + CString error; + auto paused = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "callFrames"))); + if (!result.IsEmpty()) { + if (result->IsArray(ecmaVm)) { + auto array = Local(result); + uint32_t len = array->Length(ecmaVm); + Local key = JSValueRef::Undefined(ecmaVm); + for (uint32_t i = 0; i < len; ++i) { + key = IntegerRef::New(ecmaVm, i); + Local result = Local(array)->Get(ecmaVm, key->ToString(ecmaVm)); + std::unique_ptr callFrame = CallFrame::Create(ecmaVm, result); + if (result.IsEmpty() || callFrame == nullptr) { + error += "'callFrames' format invalid;"; + } + paused->callFrames_.emplace_back(std::move(callFrame)); + } + } else { + error += "'callFrames' should a Array;"; + } + } else { + error += "should contain 'callFrames';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "reason"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + paused->reason_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'reason' should a String;"; + } + } else { + error += "should contain 'reason';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "data"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + paused->data_ = Local(result); + } else { + error += "'data' should a Object;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "hitBreakpoints"))); + if (!result.IsEmpty()) { + if (result->IsArray(ecmaVm)) { + CVector breakPoints; + auto array = Local(result); + uint32_t len = array->Length(ecmaVm); + Local key = JSValueRef::Undefined(ecmaVm); + for (uint32_t i = 0; i < len; ++i) { + key = IntegerRef::New(ecmaVm, i); + Local result = Local(array)->Get(ecmaVm, key->ToString(ecmaVm)); + if (result.IsEmpty()) { + error += "'hitBreakpoints' format invalid;"; + } + breakPoints.emplace_back(DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString())); + } + paused->hitBreakpoints_ = std::move(breakPoints); + } else { + error += "'hitBreakpoints' should a Array;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "Parsed::Create " << error; + return nullptr; + } + + return paused; +} + +Local Paused::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + size_t len = callFrames_.size(); + Local values = ArrayRef::New(ecmaVm, len); + for (size_t i = 0; i < len; i++) { + Local callFrame = callFrames_[i]->ToObject(ecmaVm); + values->Set(ecmaVm, i, callFrame); + } + params->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "callFrames")), values); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "reason")), + Local(StringRef::NewFromUtf8(ecmaVm, reason_.c_str()))); + if (data_) { + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "data")), Local(data_.value())); + } + if (hitBreakpoints_) { + len = hitBreakpoints_->size(); + values = ArrayRef::New(ecmaVm, len); + for (size_t i = 0; i < len; i++) { + Local id = StringRef::NewFromUtf8(ecmaVm, hitBreakpoints_.value()[i].c_str()); + values->Set(ecmaVm, i, id); + } + params->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "hitBreakpoints")), values); + } + + Local object = NewObject(ecmaVm); + object->Set(ecmaVm, + StringRef::NewFromUtf8(ecmaVm, "method"), + Local(StringRef::NewFromUtf8(ecmaVm, GetName().c_str()))); + object->Set(ecmaVm, StringRef::NewFromUtf8(ecmaVm, "params"), Local(params)); + + return object; +} + +std::unique_ptr Resumed::Create(const EcmaVM *ecmaVm, [[maybe_unused]] const Local ¶ms) +{ + return std::make_unique(); +} + +Local Resumed::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + Local object = NewObject(ecmaVm); + object->Set(ecmaVm, + StringRef::NewFromUtf8(ecmaVm, "method"), + Local(StringRef::NewFromUtf8(ecmaVm, GetName().c_str()))); + object->Set(ecmaVm, StringRef::NewFromUtf8(ecmaVm, "params"), Local(params)); + + return object; +} + +std::unique_ptr ScriptFailedToParse::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "ScriptFailedToParse::Create params is nullptr"; + return nullptr; + } + CString error; + auto scriptEvent = std::make_unique(); + + Local result = Local(params)->Get(ecmaVm, StringRef::NewFromUtf8(ecmaVm, "scriptId")); + if (!result.IsEmpty()) { + if (result->IsString()) { + scriptEvent->scriptId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'scriptId' should a String;"; + } + } else { + error += "should contain 'scriptId';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "url"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + scriptEvent->url_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'url' should a String;"; + } + } else { + error += "should contain 'url';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "startLine"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->startLine_ = static_cast(Local(result)->Value()); + } else { + error += "'startLine' should a Number;"; + } + } else { + error += "should contain 'startLine';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "startColumn"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->startColumn_ = static_cast(Local(result)->Value()); + } else { + error += "'startColumn' should a Number;"; + } + } else { + error += "should contain 'startColumn';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "endLine"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->endLine_ = static_cast(Local(result)->Value()); + } else { + error += "'endLine' should a Number;"; + } + } else { + error += "should contain 'endLine';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "endColumn"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->endColumn_ = static_cast(Local(result)->Value()); + } else { + error += "'endColumn' should a Number;"; + } + } else { + error += "should contain 'endColumn';"; + } + result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "executionContextId"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->executionContextId_ = static_cast(Local(result)->Value()); + } else { + error += "'executionContextId' should a Number;"; + } + } else { + error += "should contain 'executionContextId';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "hash"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + scriptEvent->hash_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'hash' should a String;"; + } + } else { + error += "should contain 'hash';"; + } + result = Local(params)->Get( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "executionContextAuxData"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + scriptEvent->execContextAuxData_ = Local(result); + } else { + error += "'executionContextAuxData' should a Object;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "sourceMapURL"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + scriptEvent->sourceMapUrl_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'sourceMapURL' should a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "hasSourceURL"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + scriptEvent->hasSourceUrl_ = result->IsTrue(); + } else { + error += "'hasSourceURL' should a Boolean;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "isModule"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + scriptEvent->isModule_ = result->IsTrue(); + } else { + error += "'isModule' should a Boolean;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "length"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->length_ = static_cast(Local(result)->Value()); + } else { + error += "'length' should a Number;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "codeOffset"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->codeOffset_ = static_cast(Local(result)->Value()); + } else { + error += "'codeOffset' should a Number;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "scriptLanguage"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + scriptEvent->scriptLanguage_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'scriptLanguage' should a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "embedderName"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + scriptEvent->embedderName_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'embedderName' should a String;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "ScriptFailedToParse::Create " << error; + return nullptr; + } + + return scriptEvent; +} + +Local ScriptFailedToParse::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "scriptId")), + Local(StringRef::NewFromUtf8(ecmaVm, scriptId_.c_str()))); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "url")), + Local(StringRef::NewFromUtf8(ecmaVm, url_.c_str()))); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "startLine")), IntegerRef::New(ecmaVm, startLine_)); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "startColumn")), + IntegerRef::New(ecmaVm, startColumn_)); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "endLine")), IntegerRef::New(ecmaVm, endLine_)); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "endColumn")), IntegerRef::New(ecmaVm, endColumn_)); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "executionContextId")), + IntegerRef::New(ecmaVm, executionContextId_)); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "hash")), + Local(StringRef::NewFromUtf8(ecmaVm, hash_.c_str()))); + if (execContextAuxData_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "executionContextAuxData")), + Local(execContextAuxData_.value())); + } + if (sourceMapUrl_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "sourceMapURL")), + Local(StringRef::NewFromUtf8(ecmaVm, sourceMapUrl_->c_str()))); + } + if (hasSourceUrl_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "hasSourceURL")), + BooleanRef::New(ecmaVm, hasSourceUrl_.value())); + } + if (isModule_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "isModule")), + BooleanRef::New(ecmaVm, isModule_.value())); + } + if (length_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "length")), + IntegerRef::New(ecmaVm, length_.value())); + } + if (codeOffset_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "codeOffset")), + IntegerRef::New(ecmaVm, codeOffset_.value())); + } + if (scriptLanguage_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "scriptLanguage")), + Local(StringRef::NewFromUtf8(ecmaVm, scriptLanguage_->c_str()))); + } + if (embedderName_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "embedderName")), + Local(StringRef::NewFromUtf8(ecmaVm, embedderName_->c_str()))); + } + + Local object = NewObject(ecmaVm); + object->Set(ecmaVm, + StringRef::NewFromUtf8(ecmaVm, "method"), + Local(StringRef::NewFromUtf8(ecmaVm, GetName().c_str()))); + object->Set(ecmaVm, StringRef::NewFromUtf8(ecmaVm, "params"), Local(params)); + + return object; +} + +std::unique_ptr ScriptParsed::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "ScriptParsed::Create params is nullptr"; + return nullptr; + } + CString error; + auto scriptEvent = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "scriptId"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + scriptEvent->scriptId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'scriptId' should a String;"; + } + } else { + error += "should contain 'scriptId';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "url"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + scriptEvent->url_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'url' should a String;"; + } + } else { + error += "should contain 'url';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "startLine"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->startLine_ = static_cast(Local(result)->Value()); + } else { + error += "'startLine' should a Number;"; + } + } else { + error += "should contain 'startLine';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "startColumn"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->startColumn_ = static_cast(Local(result)->Value()); + } else { + error += "'startColumn' should a Number;"; + } + } else { + error += "should contain 'startColumn';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "endLine"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->endLine_ = static_cast(Local(result)->Value()); + } else { + error += "'endLine' should a Number;"; + } + } else { + error += "should contain 'endLine';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "endColumn"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->endColumn_ = static_cast(Local(result)->Value()); + } else { + error += "'endColumn' should a Number;"; + } + } else { + error += "should contain 'endColumn';"; + } + result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "executionContextId"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->executionContextId_ = static_cast(Local(result)->Value()); + } else { + error += "'executionContextId' should a Number;"; + } + } else { + error += "should contain 'executionContextId';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "hash"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + scriptEvent->hash_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'hash' should a String;"; + } + } else { + error += "should contain 'hash';"; + } + result = Local(params)->Get( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "executionContextAuxData"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + scriptEvent->execContextAuxData_ = Local(result); + } else { + error += "'executionContextAuxData' should a Object;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "isLiveEdit"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + scriptEvent->isLiveEdit_ = result->IsTrue(); + } else { + error += "'isLiveEdit' should a Boolean;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "sourceMapURL"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + scriptEvent->sourceMapUrl_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'sourceMapURL' should a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "hasSourceURL"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + scriptEvent->hasSourceUrl_ = result->IsTrue(); + } else { + error += "'hasSourceURL' should a Boolean;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "isModule"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + scriptEvent->isModule_ = result->IsTrue(); + } else { + error += "'isModule' should a Boolean;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "length"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->length_ = static_cast(Local(result)->Value()); + } else { + error += "'length' should a Number;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "codeOffset"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptEvent->codeOffset_ = static_cast(Local(result)->Value()); + } else { + error += "'codeOffset' should a Number;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "scriptLanguage"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + scriptEvent->scriptLanguage_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'scriptLanguage' should a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "embedderName"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + scriptEvent->embedderName_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'embedderName' should a String;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "ScriptParsed::Create " << error; + return nullptr; + } + + return scriptEvent; +} + +Local ScriptParsed::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "scriptId")), + Local(StringRef::NewFromUtf8(ecmaVm, scriptId_.c_str()))); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "url")), + Local(StringRef::NewFromUtf8(ecmaVm, url_.c_str()))); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "startLine")), IntegerRef::New(ecmaVm, startLine_)); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "startColumn")), + IntegerRef::New(ecmaVm, startColumn_)); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "endLine")), IntegerRef::New(ecmaVm, endLine_)); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "endColumn")), IntegerRef::New(ecmaVm, endColumn_)); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "executionContextId")), + IntegerRef::New(ecmaVm, executionContextId_)); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "hash")), + Local(StringRef::NewFromUtf8(ecmaVm, hash_.c_str()))); + if (execContextAuxData_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "executionContextAuxData")), + Local(execContextAuxData_.value())); + } + if (isLiveEdit_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "isLiveEdit")), + BooleanRef::New(ecmaVm, isLiveEdit_.value())); + } + if (sourceMapUrl_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "sourceMapURL")), + Local(StringRef::NewFromUtf8(ecmaVm, sourceMapUrl_->c_str()))); + } + if (hasSourceUrl_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "hasSourceURL")), + BooleanRef::New(ecmaVm, hasSourceUrl_.value())); + } + if (isModule_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "isModule")), + BooleanRef::New(ecmaVm, isModule_.value())); + } + if (length_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "length")), + IntegerRef::New(ecmaVm, length_.value())); + } + if (codeOffset_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "codeOffset")), + IntegerRef::New(ecmaVm, codeOffset_.value())); + } + if (scriptLanguage_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "scriptLanguage")), + Local(StringRef::NewFromUtf8(ecmaVm, scriptLanguage_->c_str()))); + } + if (embedderName_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "embedderName")), + Local(StringRef::NewFromUtf8(ecmaVm, embedderName_->c_str()))); + } + Local object = NewObject(ecmaVm); + object->Set(ecmaVm, + StringRef::NewFromUtf8(ecmaVm, "method"), + Local(StringRef::NewFromUtf8(ecmaVm, GetName().c_str()))); + object->Set(ecmaVm, StringRef::NewFromUtf8(ecmaVm, "params"), Local(params)); + + return object; +} +} // namespace panda::tooling::ecmascript diff --git a/ecmascript/tooling/base/pt_events.h b/ecmascript/tooling/base/pt_events.h new file mode 100644 index 0000000000000000000000000000000000000000..baa79a38fbcc276e427f8ba219105a22fcdd8e09 --- /dev/null +++ b/ecmascript/tooling/base/pt_events.h @@ -0,0 +1,740 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_EVENTS_H +#define PANDA_TOOLING_ECMASCRIPT_EVENTS_H + +#include +#include + +#include "libpandabase/macros.h" +#include "ecmascript/tooling/base/pt_types.h" +#include "ecmascript/tooling/dispatcher.h" +#include "ecmascript/mem/c_containers.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::EcmaVM; + +class PtBaseEvents : public PtBaseTypes { +public: + PtBaseEvents() = default; + ~PtBaseEvents() override = default; + Local ToObject(const EcmaVM *ecmaVm) override = 0; + virtual CString GetName() = 0; + +private: + NO_COPY_SEMANTIC(PtBaseEvents); + NO_MOVE_SEMANTIC(PtBaseEvents); +}; + +class BreakpointResolved final : public PtBaseEvents { +public: + BreakpointResolved() = default; + ~BreakpointResolved() override = default; + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + CString GetName() override + { + return "Debugger.breakpointResolved"; + } + + BreakpointId GetBreakpointId() const + { + return breakpointId_; + } + + BreakpointResolved &SetBreakpointId(const BreakpointId &breakpointId) + { + breakpointId_ = breakpointId; + return *this; + } + + Location *GetLocation() const + { + return location_.get(); + } + + BreakpointResolved &SetLocation(std::unique_ptr location) + { + location_ = std::move(location); + return *this; + } + +private: + NO_COPY_SEMANTIC(BreakpointResolved); + NO_MOVE_SEMANTIC(BreakpointResolved); + + BreakpointId breakpointId_ {}; + std::unique_ptr location_ {nullptr}; +}; + +class Paused final : public PtBaseEvents { +public: + Paused() = default; + ~Paused() override = default; + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + CString GetName() override + { + return "Debugger.paused"; + } + + const CVector> *GetCallFrames() const + { + return &callFrames_; + } + + Paused &SetCallFrames(CVector> call_frames) + { + callFrames_ = std::move(call_frames); + return *this; + } + + CString GetReason() const + { + return reason_; + } + + Paused &SetReason(PauseReason reason) + { + reason_ = GetReasonString(reason); + return *this; + } + + static CString GetReasonString(PauseReason reason) + { + switch (reason) { + case AMBIGUOUS: { + return "ambiguous"; + } + case ASSERT: { + return "assert"; + } + case DEBUGCOMMAND: { + return "debugCommand"; + } + case DOM: { + return "DOM"; + } + case EVENTLISTENER: { + return "EventListener"; + } + case EXCEPTION: { + return "exception"; + } + case INSTRUMENTATION: { + return "instrumentation"; + } + case OOM: { + return "OOM"; + } + case OTHER: { + return "other"; + } + case PROMISEREJECTION: { + return "promiseRejection"; + } + case XHR: { + return "XHR"; + } + case BREAK_ON_START: { + return "Break on start"; + } + default: { + LOG(ERROR, DEBUGGER) << "Unknown paused reason: " << reason; + } + } + return ""; + } + + Local GetData() const + { + return data_.value_or(Local()); + } + + Paused &SetData(const Local &data) + { + data_ = data; + return *this; + } + + bool HasData() const + { + return data_.has_value(); + } + + CVector GetHitBreakpoints() const + { + return hitBreakpoints_.value_or(CVector()); + } + + Paused &SetHitBreakpoints(CVector hitBreakpoints) + { + hitBreakpoints_ = std::move(hitBreakpoints); + return *this; + } + + bool HasHitBreakpoints() const + { + return hitBreakpoints_.has_value(); + } + +private: + NO_COPY_SEMANTIC(Paused); + NO_MOVE_SEMANTIC(Paused); + + CVector> callFrames_ {}; + CString reason_ {}; + std::optional> data_ {}; + std::optional> hitBreakpoints_ {}; +}; + +class Resumed final : public PtBaseEvents { +public: + Resumed() = default; + ~Resumed() override = default; + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + CString GetName() override + { + return "Debugger.resumed"; + } + +private: + NO_COPY_SEMANTIC(Resumed); + NO_MOVE_SEMANTIC(Resumed); +}; + +class ScriptFailedToParse final : public PtBaseEvents { +public: + ScriptFailedToParse() = default; + ~ScriptFailedToParse() override = default; + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + CString GetName() override + { + return "Debugger.scriptFailedToParse"; + } + + ScriptId GetScriptId() const + { + return scriptId_; + } + + ScriptFailedToParse &SetScriptId(const ScriptId &scriptId) + { + scriptId_ = scriptId; + return *this; + } + + CString GetUrl() const + { + return url_; + } + + ScriptFailedToParse &SetUrl(const CString &url) + { + url_ = url; + return *this; + } + + int32_t GetStartLine() const + { + return startLine_; + } + + ScriptFailedToParse &SetStartLine(int32_t startLine) + { + startLine_ = startLine; + return *this; + } + + int32_t GetStartColumn() const + { + return startColumn_; + } + + ScriptFailedToParse &SetStartColumn(int32_t startColumn) + { + startColumn_ = startColumn; + return *this; + } + + int32_t GetEndLine() const + { + return endLine_; + } + + ScriptFailedToParse &SetEndLine(int32_t endLine) + { + endLine_ = endLine; + return *this; + } + + int32_t GetEndColumn() const + { + return endColumn_; + } + + ScriptFailedToParse &SetEndColumn(int32_t endColumn) + { + endColumn_ = endColumn; + return *this; + } + + int32_t GetExecutionContextId() const + { + return executionContextId_; + } + + ScriptFailedToParse &SetExecutionContextId(int32_t executionContextId) + { + executionContextId_ = executionContextId; + return *this; + } + + CString GetHash() const + { + return hash_; + } + + ScriptFailedToParse &SetHash(const CString &hash) + { + hash_ = hash; + return *this; + } + + Local GetExecutionContextAuxData() const + { + return execContextAuxData_.value_or(Local()); + } + + ScriptFailedToParse &SetExecutionContextAuxData(const Local &execContextAuxData) + { + execContextAuxData_ = execContextAuxData; + return *this; + } + + bool HasExecutionContextAuxData() const + { + return execContextAuxData_.has_value(); + } + + CString GetSourceMapURL() const + { + return sourceMapUrl_.value_or(""); + } + + ScriptFailedToParse &SetSourceMapURL(const CString &sourceMapUrl) + { + sourceMapUrl_ = sourceMapUrl; + return *this; + } + + bool HasSourceMapURL() const + { + return sourceMapUrl_.has_value(); + } + + bool GetHasSourceURL() const + { + return hasSourceUrl_.value_or(false); + } + + ScriptFailedToParse &SetHasSourceURL(bool hasSourceUrl) + { + hasSourceUrl_ = hasSourceUrl; + return *this; + } + + bool HasHasSourceURL() const + { + return hasSourceUrl_.has_value(); + } + + bool GetIsModule() const + { + return isModule_.value_or(false); + } + + ScriptFailedToParse &SetIsModule(bool isModule) + { + isModule_ = isModule; + return *this; + } + + bool HasIsModule() const + { + return isModule_.has_value(); + } + + int32_t GetLength() const + { + return length_.value_or(0); + } + + ScriptFailedToParse &SetLength(int32_t length) + { + length_ = length; + return *this; + } + + bool HasLength() const + { + return length_.has_value(); + } + + int32_t GetCodeOffset() const + { + return codeOffset_.value_or(0); + } + + ScriptFailedToParse &SetCodeOffset(int32_t codeOffset) + { + codeOffset_ = codeOffset; + return *this; + } + + bool HasCodeOffset() const + { + return codeOffset_.has_value(); + } + + CString GetScriptLanguage() const + { + return scriptLanguage_.value_or(""); + } + + ScriptFailedToParse &SetScriptLanguage(const CString &scriptLanguage) + { + scriptLanguage_ = scriptLanguage; + return *this; + } + + bool HasScriptLanguage() const + { + return scriptLanguage_.has_value(); + } + + CString GetEmbedderName() const + { + return embedderName_.value_or(""); + } + + ScriptFailedToParse &SetEmbedderName(const CString &embedderName) + { + embedderName_ = embedderName; + return *this; + } + + bool HasEmbedderName() const + { + return embedderName_.has_value(); + } + +private: + NO_COPY_SEMANTIC(ScriptFailedToParse); + NO_MOVE_SEMANTIC(ScriptFailedToParse); + + ScriptId scriptId_ {}; + CString url_ {}; + int32_t startLine_ {0}; + int32_t startColumn_ {0}; + int32_t endLine_ {0}; + int32_t endColumn_ {0}; + ExecutionContextId executionContextId_ {0}; + CString hash_ {}; + std::optional> execContextAuxData_ {}; + std::optional sourceMapUrl_ {}; + std::optional hasSourceUrl_ {}; + std::optional isModule_ {}; + std::optional length_ {}; + std::optional codeOffset_ {}; + std::optional scriptLanguage_ {}; + std::optional embedderName_ {}; +}; + +class ScriptParsed final : public PtBaseEvents { +public: + ScriptParsed() = default; + ~ScriptParsed() override = default; + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + CString GetName() override + { + return "Debugger.scriptParsed"; + } + + ScriptId GetScriptId() const + { + return scriptId_; + } + + ScriptParsed &SetScriptId(const ScriptId &scriptId) + { + scriptId_ = scriptId; + return *this; + } + + CString GetUrl() const + { + return url_; + } + + ScriptParsed &SetUrl(const CString &url) + { + url_ = url; + return *this; + } + + int32_t GetStartLine() const + { + return startLine_; + } + + ScriptParsed &SetStartLine(int32_t startLine) + { + startLine_ = startLine; + return *this; + } + + int32_t GetStartColumn() const + { + return startColumn_; + } + + ScriptParsed &SetStartColumn(int32_t startColumn) + { + startColumn_ = startColumn; + return *this; + } + + int32_t GetEndLine() const + { + return endLine_; + } + + ScriptParsed &SetEndLine(int32_t endLine) + { + endLine_ = endLine; + return *this; + } + + int32_t GetEndColumn() const + { + return endColumn_; + } + + ScriptParsed &SetEndColumn(int32_t endColumn) + { + endColumn_ = endColumn; + return *this; + } + + int32_t GetExecutionContextId() const + { + return executionContextId_; + } + + ScriptParsed &SetExecutionContextId(int32_t executionContextId) + { + executionContextId_ = executionContextId; + return *this; + } + + CString GetHash() const + { + return hash_; + } + + ScriptParsed &SetHash(const CString &hash) + { + hash_ = hash; + return *this; + } + + bool GetIsLiveEdit() const + { + return isLiveEdit_.value_or(false); + } + + ScriptParsed &SetIsLiveEdit(bool isLiveEdit) + { + isLiveEdit_ = isLiveEdit; + return *this; + } + + bool HasIsLiveEdit() const + { + return isLiveEdit_.has_value(); + } + + Local GetExecutionContextAuxData() const + { + return execContextAuxData_.value_or(Local()); + } + + ScriptParsed &SetExecutionContextAuxData(const Local &execContextAuxData) + { + execContextAuxData_ = execContextAuxData; + return *this; + } + + bool HasExecutionContextAuxData() const + { + return execContextAuxData_.has_value(); + } + + CString GetSourceMapURL() const + { + return sourceMapUrl_.value_or(""); + } + + ScriptParsed &SetSourceMapURL(const CString &sourceMapUrl) + { + sourceMapUrl_ = sourceMapUrl; + return *this; + } + + bool HasSourceMapURL() const + { + return sourceMapUrl_.has_value(); + } + + bool GetHasSourceURL() const + { + return hasSourceUrl_.value_or(false); + } + + ScriptParsed &SetHasSourceURL(bool hasSourceUrl) + { + hasSourceUrl_ = hasSourceUrl; + return *this; + } + + bool HasHasSourceURL() const + { + return hasSourceUrl_.has_value(); + } + + bool GetIsModule() const + { + return isModule_.value_or(false); + } + + ScriptParsed &SetIsModule(bool isModule) + { + isModule_ = isModule; + return *this; + } + + bool HasIsModule() const + { + return isModule_.has_value(); + } + + int32_t GetLength() const + { + return length_.value_or(0); + } + + ScriptParsed &SetLength(int32_t length) + { + length_ = length; + return *this; + } + + bool HasLength() const + { + return length_.has_value(); + } + + int32_t GetCodeOffset() const + { + return codeOffset_.value_or(0); + } + + ScriptParsed &SetCodeOffset(int32_t codeOffset) + { + codeOffset_ = codeOffset; + return *this; + } + + bool HasCodeOffset() const + { + return codeOffset_.has_value(); + } + + CString GetScriptLanguage() const + { + return scriptLanguage_.value_or(""); + } + + ScriptParsed &SetScriptLanguage(const CString &scriptLanguage) + { + scriptLanguage_ = scriptLanguage; + return *this; + } + + bool HasScriptLanguage() const + { + return scriptLanguage_.has_value(); + } + + CString GetEmbedderName() const + { + return embedderName_.value_or(""); + } + + ScriptParsed &SetEmbedderName(const CString &embedderName) + { + embedderName_ = embedderName; + return *this; + } + + bool HasEmbedderName() const + { + return embedderName_.has_value(); + } + +private: + NO_COPY_SEMANTIC(ScriptParsed); + NO_MOVE_SEMANTIC(ScriptParsed); + + ScriptId scriptId_ {}; + CString url_ {}; + int32_t startLine_ {0}; + int32_t startColumn_ {0}; + int32_t endLine_ {0}; + int32_t endColumn_ {0}; + ExecutionContextId executionContextId_ {0}; + CString hash_ {}; + std::optional> execContextAuxData_ {}; + std::optional isLiveEdit_ {}; + std::optional sourceMapUrl_ {}; + std::optional hasSourceUrl_ {}; + std::optional isModule_ {}; + std::optional length_ {}; + std::optional codeOffset_ {}; + std::optional scriptLanguage_ {}; + std::optional embedderName_ {}; +}; +} // namespace panda::tooling::ecmascript +#endif diff --git a/ecmascript/tooling/base/pt_params.cpp b/ecmascript/tooling/base/pt_params.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3db16557c4263bd10eff4b53e81d2109bff1f405 --- /dev/null +++ b/ecmascript/tooling/base/pt_params.cpp @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/base/pt_params.h" + +namespace panda::tooling::ecmascript { +std::unique_ptr EnableParams::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "maxScriptsCacheSize"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + paramsObject->maxScriptsCacheSize_ = Local(result)->Value(); + } else { + error += "'maxScriptsCacheSize' should be a Number;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "EnableParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr EvaluateOnCallFrameParams::Create( + const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "EvaluateOnCallFrameParams::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "callFrameId"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + paramsObject->callFrameId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'callframeid' should be a String;"; + } + } else { + error += "should contain 'callframeid';"; + } + + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "expression"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + paramsObject->expression_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'expression' should be a String;"; + } + } else { + error += "should contain 'expression';"; + } + + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "objectGroup"))); + if (!result.IsEmpty() && result->IsString()) { + paramsObject->objectGroup_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } + + result = Local(params)->Get( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "includeCommandLineAPI"))); + if (!result.IsEmpty() && result->IsBoolean()) { + paramsObject->includeCommandLineApi_ = result->IsTrue(); + } + + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "silent"))); + if (!result.IsEmpty() && result->IsBoolean()) { + paramsObject->silent_ = result->IsTrue(); + } + + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "returnByValue"))); + if (!result.IsEmpty() && result->IsBoolean()) { + paramsObject->returnByValue_ = result->IsTrue(); + } + + result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "generatePreview"))); + if (!result.IsEmpty() && result->IsBoolean()) { + paramsObject->generatePreview_ = result->IsTrue(); + } + + result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "throwOnSideEffect"))); + if (!result.IsEmpty() && result->IsBoolean()) { + paramsObject->throwOnSideEffect_ = result->IsTrue(); + } + + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "EvaluateOnCallFrameParams::Create " << error; + return nullptr; + } + return paramsObject; +} + +std::unique_ptr GetPossibleBreakpointsParams::Create( + const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "start"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = Location::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'start' format error;"; + } else { + paramsObject->start_ = std::move(obj); + } + } else { + error += "'start' should be an Object;"; + } + } else { + error += "should contain 'start';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "end"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = Location::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'end' format error;"; + } else { + paramsObject->end_ = std::move(obj); + } + } else { + error += "'end' should be an Object;"; + } + } + result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "restrictToFunction"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + paramsObject->restrictToFunction_ = result->IsTrue(); + } else { + error += "'restrictToFunction' should be a Boolean;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "GetPossibleBreakpointsParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr GetScriptSourceParams::Create( + const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "scriptId"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + paramsObject->scriptId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'scriptId' should be a String;"; + } + } else { + error += "should contain 'scriptId';"; + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "GetScriptSourceParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr RemoveBreakpointParams::Create( + const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "breakpointId"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + paramsObject->breakpointId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'breakpointId' should be a String;"; + } + } else { + error += "should contain 'breakpointId';"; + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "RemoveBreakpointParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr ResumeParams::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "terminateOnResume"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + paramsObject->terminateOnResume_ = result->IsTrue(); + } else { + error += "'terminateOnResume' should be a Boolean;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "ResumeParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr SetAsyncCallStackDepthParams::Create( + const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "maxDepth"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + paramsObject->maxDepth_ = static_cast(Local(result)->Value()); + } else { + error += "'maxDepth' should be a Number;"; + } + } else { + error += "should contain 'maxDepth';"; + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "SetAsyncCallStackDepthParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr SetBlackboxPatternsParams::Create( + const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "patterns"))); + if (!result.IsEmpty()) { + if (result->IsArray(ecmaVm)) { + Local array = Local(result); + uint32_t len = array->Length(ecmaVm); + Local key = JSValueRef::Undefined(ecmaVm); + for (array_size_t i = 0; i < len; i++) { + key = IntegerRef::New(ecmaVm, i); + Local value = Local(array)->Get(ecmaVm, key->ToString(ecmaVm)); + if (value->IsString()) { + paramsObject->patterns_.emplace_back( + DebuggerApi::ConvertToString(StringRef::Cast(*value)->ToString())); + } else { + error += "'patterns' items should be a String;"; + } + } + } else { + error += "'patterns' should be an Array;"; + } + } else { + error += "should contain 'patterns';"; + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "SetBlackboxPatternsParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr SetBreakpointByUrlParams::Create( + const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "lineNumber"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + paramsObject->line_ = static_cast(Local(result)->Value()); + } else { + error += "'lineNumber' should be a Number;"; + } + } else { + error += "should contain 'lineNumber';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "url"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + paramsObject->url_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'url' should be a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "urlRegex"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + paramsObject->urlRegex_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'urlRegex' should be a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "scriptHash"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + paramsObject->scriptHash_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'scriptHash' should be a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "columnNumber"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + paramsObject->column_ = static_cast(Local(result)->Value()); + } else { + error += "'columnNumber' should be a Number;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "condition"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + paramsObject->condition_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'condition' should be a String;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "SetBreakpointByUrlParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr SetPauseOnExceptionsParams::Create( + const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "state"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + if (!paramsObject->StoreState(DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()))) { + error += "'state' is invalid;"; + } + } else { + error += "'state' should be a String;"; + } + } else { + error += "should contain 'state';"; + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "SetPauseOnExceptionsParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr StepIntoParams::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "breakOnAsyncCall"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + paramsObject->breakOnAsyncCall_ = result->IsTrue(); + } else { + error += "'terminateOnResume' should be a Boolean;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "skipList"))); + if (!result.IsEmpty()) { + if (result->IsArray(ecmaVm)) { + Local array = Local(result); + uint32_t len = array->Length(ecmaVm); + Local key = JSValueRef::Undefined(ecmaVm); + for (array_size_t i = 0; i < len; i++) { + key = IntegerRef::New(ecmaVm, i); + Local value = Local(array)->Get(ecmaVm, key->ToString(ecmaVm)); + if (value->IsObject()) { + std::unique_ptr obj = LocationRange::Create(ecmaVm, value); + if (obj != nullptr) { + paramsObject->skipList_->emplace_back(std::move(obj)); + } else { + error += "'skipList' items LocationRange is invalid;"; + } + } else { + error += "'skipList' items should be an Object;"; + } + } + } else { + error += "'skipList' should be an Array;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "StepIntoParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr StepOverParams::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "skipList"))); + if (!result.IsEmpty()) { + if (result->IsArray(ecmaVm)) { + Local array = Local(result); + uint32_t len = array->Length(ecmaVm); + Local key = JSValueRef::Undefined(ecmaVm); + for (array_size_t i = 0; i < len; i++) { + key = IntegerRef::New(ecmaVm, i); + Local value = Local(array)->Get(ecmaVm, key->ToString(ecmaVm)); + if (value->IsObject()) { + std::unique_ptr obj = LocationRange::Create(ecmaVm, value); + if (obj != nullptr) { + paramsObject->skipList_->emplace_back(std::move(obj)); + } else { + error += "'skipList' items LocationRange is invaild;"; + } + } else { + error += "'skipList' items should be an Object;"; + } + } + } else { + error += "'skipList' should be an Array;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "StepOverParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + +std::unique_ptr GetPropertiesParams::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + ASSERT(ecmaVm); + if (params.IsEmpty()) { + LOG(ERROR, DEBUGGER) << "GetPropertiesParams::Create params is nullptr"; + return nullptr; + } + CString error; + auto paramsObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "objectId"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + paramsObject->objectId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'objectId' should be a String;"; + } + } else { + error += "should contain 'objectId';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "ownProperties"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + paramsObject->ownProperties_ = result->IsTrue(); + } else { + error += "'ownProperties' should be a Boolean;"; + } + } + result = Local(params)->Get( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "accessorPropertiesOnly"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + paramsObject->accessorPropertiesOnly_ = result->IsTrue(); + } else { + error += "'accessorPropertiesOnly' should be a Boolean;"; + } + } + result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "generatePreview"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + paramsObject->generatePreview_ = result->IsTrue(); + } else { + error += "'generatePreview' should be a Boolean;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "GetPropertiesParams::Create " << error; + return nullptr; + } + + return paramsObject; +} +} // namespace panda::tooling::ecmascript diff --git a/ecmascript/tooling/base/pt_params.h b/ecmascript/tooling/base/pt_params.h new file mode 100644 index 0000000000000000000000000000000000000000..624dad17bf22391a6cfd6ad38b69ac49d2965747 --- /dev/null +++ b/ecmascript/tooling/base/pt_params.h @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_PARAMS_H +#define PANDA_TOOLING_ECMASCRIPT_PARAMS_H + +#include "ecmascript/tooling/base/pt_types.h" + +namespace panda::tooling::ecmascript { +class PtBaseParams : public PtBaseTypes { +public: + PtBaseParams() = default; + ~PtBaseParams() override = default; + + virtual Local ToObject(const EcmaVM *ecmaVm) override = 0; + +private: + NO_COPY_SEMANTIC(PtBaseParams); + NO_MOVE_SEMANTIC(PtBaseParams); +}; + +class EnableParams : public PtBaseParams { +public: + EnableParams() = default; + ~EnableParams() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + double GetMaxScriptsCacheSize() const + { + return maxScriptsCacheSize_.value_or(0); + } + + bool HasMaxScriptsCacheSize() const + { + return maxScriptsCacheSize_.has_value(); + } + +private: + NO_COPY_SEMANTIC(EnableParams); + NO_MOVE_SEMANTIC(EnableParams); + + std::optional maxScriptsCacheSize_ {0}; +}; + +class EvaluateOnCallFrameParams : public PtBaseParams { +public: + EvaluateOnCallFrameParams() = default; + ~EvaluateOnCallFrameParams() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + CString GetCallFrameId() + { + return callFrameId_; + } + + CString GetExpression() + { + return expression_; + } + +private: + NO_COPY_SEMANTIC(EvaluateOnCallFrameParams); + NO_MOVE_SEMANTIC(EvaluateOnCallFrameParams); + + CString callFrameId_ {}; + CString expression_ {}; + std::optional objectGroup_ {}; + std::optional includeCommandLineApi_ {}; + std::optional silent_ {}; + std::optional returnByValue_ {}; + std::optional generatePreview_ {}; + std::optional throwOnSideEffect_ {}; +}; + +class GetPossibleBreakpointsParams : public PtBaseParams { +public: + GetPossibleBreakpointsParams() = default; + ~GetPossibleBreakpointsParams() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + Location *GetStart() const + { + return start_.get(); + } + + Location *GetEnd() const + { + if (end_) { + return end_->get(); + } + return nullptr; + } + + bool HasEnd() const + { + return end_.has_value(); + } + + bool GetRestrictToFunction() const + { + return restrictToFunction_.value_or(false); + } + + bool HasRestrictToFunction() const + { + return restrictToFunction_.has_value(); + } + +private: + NO_COPY_SEMANTIC(GetPossibleBreakpointsParams); + NO_MOVE_SEMANTIC(GetPossibleBreakpointsParams); + + std::unique_ptr start_ {nullptr}; + std::optional> end_ {}; + std::optional restrictToFunction_ {}; +}; + +class GetScriptSourceParams : public PtBaseParams { +public: + GetScriptSourceParams() = default; + ~GetScriptSourceParams() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + ScriptId GetScriptId() const + { + return scriptId_; + } + +private: + NO_COPY_SEMANTIC(GetScriptSourceParams); + NO_MOVE_SEMANTIC(GetScriptSourceParams); + + ScriptId scriptId_ {}; +}; + +class RemoveBreakpointParams : public PtBaseParams { +public: + RemoveBreakpointParams() = default; + ~RemoveBreakpointParams() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + BreakpointId GetBreakpointId() const + { + return breakpointId_; + } + +private: + NO_COPY_SEMANTIC(RemoveBreakpointParams); + NO_MOVE_SEMANTIC(RemoveBreakpointParams); + + BreakpointId breakpointId_ {}; +}; + +class ResumeParams : public PtBaseParams { +public: + ResumeParams() = default; + ~ResumeParams() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + bool GetTerminateOnResume() const + { + return terminateOnResume_.value_or(false); + } + + bool HasTerminateOnResume() const + { + return terminateOnResume_.has_value(); + } + +private: + NO_COPY_SEMANTIC(ResumeParams); + NO_MOVE_SEMANTIC(ResumeParams); + + std::optional terminateOnResume_ {}; +}; + +class SetAsyncCallStackDepthParams : public PtBaseParams { +public: + SetAsyncCallStackDepthParams() = default; + ~SetAsyncCallStackDepthParams() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + int32_t GetMaxDepth() const + { + return maxDepth_; + } + +private: + NO_COPY_SEMANTIC(SetAsyncCallStackDepthParams); + NO_MOVE_SEMANTIC(SetAsyncCallStackDepthParams); + + int32_t maxDepth_ {0}; +}; + +class SetBlackboxPatternsParams : public PtBaseParams { +public: + SetBlackboxPatternsParams() = default; + ~SetBlackboxPatternsParams() override = default; + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + CList GetPatterns() const + { + return patterns_; + } + +private: + NO_COPY_SEMANTIC(SetBlackboxPatternsParams); + NO_MOVE_SEMANTIC(SetBlackboxPatternsParams); + + CList patterns_ {}; +}; + +class SetBreakpointByUrlParams : public PtBaseParams { +public: + SetBreakpointByUrlParams() = default; + ~SetBreakpointByUrlParams() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + int32_t GetLine() const + { + return line_; + } + + CString GetUrl() const + { + return url_.value_or(""); + } + + bool HasUrl() const + { + return url_.has_value(); + } + + CString GetUrlRegex() const + { + return urlRegex_.value_or(""); + } + + bool HasUrlRegex() const + { + return urlRegex_.has_value(); + } + + CString GetScriptHash() const + { + return scriptHash_.value_or(""); + } + + bool HasScriptHash() const + { + return scriptHash_.has_value(); + } + + int32_t GetColumn() const + { + return column_.value_or(0); + } + + bool HasColumn() const + { + return column_.has_value(); + } + + CString GetCondition() const + { + return condition_.value_or(""); + } + + bool HasCondition() const + { + return condition_.has_value(); + } + +private: + NO_COPY_SEMANTIC(SetBreakpointByUrlParams); + NO_MOVE_SEMANTIC(SetBreakpointByUrlParams); + + int32_t line_ {0}; + std::optional url_ {}; + std::optional urlRegex_ {}; + std::optional scriptHash_ {}; + std::optional column_ {0}; + std::optional condition_ {}; +}; + +enum class PauseOnExceptionsState : uint8_t { NONE, UNCAUGHT, ALL }; + +class SetPauseOnExceptionsParams : public PtBaseParams { +public: + SetPauseOnExceptionsParams() = default; + ~SetPauseOnExceptionsParams() override = default; + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + PauseOnExceptionsState GetState() const + { + return state_; + } + + bool StoreState(const CString &state) + { + if (state == "none") { + state_ = PauseOnExceptionsState::NONE; + return true; + } + if (state == "uncaught") { + state_ = PauseOnExceptionsState::UNCAUGHT; + return true; + } + if (state == "all") { + state_ = PauseOnExceptionsState::ALL; + return true; + } + return false; + } + +private: + NO_COPY_SEMANTIC(SetPauseOnExceptionsParams); + NO_MOVE_SEMANTIC(SetPauseOnExceptionsParams); + + PauseOnExceptionsState state_ {PauseOnExceptionsState::ALL}; +}; + +class StepIntoParams : public PtBaseParams { +public: + StepIntoParams() = default; + ~StepIntoParams() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + bool GetBreakOnAsyncCall() const + { + return breakOnAsyncCall_.value_or(false); + } + + bool HasBreakOnAsyncCall() const + { + return breakOnAsyncCall_.has_value(); + } + + const CList> *GetSkipList() const + { + if (!skipList_) { + return nullptr; + } + return &(skipList_.value()); + } + + bool HasSkipList() const + { + return skipList_.has_value(); + } + +private: + NO_COPY_SEMANTIC(StepIntoParams); + NO_MOVE_SEMANTIC(StepIntoParams); + + std::optional breakOnAsyncCall_ {}; + std::optional>> skipList_ {}; +}; + +class StepOverParams : public PtBaseParams { +public: + StepOverParams() = default; + ~StepOverParams() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + const CList> *GetSkipList() const + { + if (!skipList_) { + return nullptr; + } + return &(skipList_.value()); + } + + bool HasSkipList() const + { + return skipList_.has_value(); + } + +private: + NO_COPY_SEMANTIC(StepOverParams); + NO_MOVE_SEMANTIC(StepOverParams); + + std::optional>> skipList_ {}; +}; + +class GetPropertiesParams : public PtBaseParams { +public: + GetPropertiesParams() = default; + ~GetPropertiesParams() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override + { + return Local(); + } + + RemoteObjectId GetObjectId() const + { + return objectId_; + } + + bool GetOwnProperties() const + { + return ownProperties_.value_or(false); + } + + bool HasOwnProperties() const + { + return ownProperties_.has_value(); + } + + bool GetAccessPropertiesOnly() const + { + return accessorPropertiesOnly_.value_or(false); + } + + bool HasAccessPropertiesOnly() const + { + return accessorPropertiesOnly_.has_value(); + } + + bool GetGeneratePreview() const + { + return generatePreview_.value_or(false); + } + + bool HasGeneratePreview() const + { + return generatePreview_.has_value(); + } + +private: + NO_COPY_SEMANTIC(GetPropertiesParams); + NO_MOVE_SEMANTIC(GetPropertiesParams); + + RemoteObjectId objectId_ {}; + std::optional ownProperties_ {}; + std::optional accessorPropertiesOnly_ {}; + std::optional generatePreview_ {}; +}; +} // namespace panda::tooling::ecmascript +#endif \ No newline at end of file diff --git a/ecmascript/tooling/base/pt_returns.cpp b/ecmascript/tooling/base/pt_returns.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a21db526f98d0c0f884691bdef334db506dc7220 --- /dev/null +++ b/ecmascript/tooling/base/pt_returns.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/base/pt_returns.h" + +namespace panda::tooling::ecmascript { +Local EnableReturns::ToObject(const EcmaVM *ecmaVm) +{ + Local result = NewObject(ecmaVm); + + result->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "debuggerId")), + Local(StringRef::NewFromUtf8(ecmaVm, debuggerId_.c_str()))); + + return result; +} + +Local SetBreakpointByUrlReturns::ToObject(const EcmaVM *ecmaVm) +{ + size_t len = locations_.size(); + Local values = ArrayRef::New(ecmaVm, len); + for (size_t i = 0; i < len; i++) { + Local location = locations_[i]->ToObject(ecmaVm); + values->Set(ecmaVm, i, location); + } + + Local result = NewObject(ecmaVm); + result->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "breakpointId")), + Local(StringRef::NewFromUtf8(ecmaVm, id_.c_str()))); + result->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "locations")), values); + + return result; +} + +Local EvaluateOnCallFrameReturns::ToObject(const EcmaVM *ecmaVm) +{ + Local result = NewObject(ecmaVm); + + Local location = result_->ToObject(ecmaVm); + result->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "result")), Local(location)); + if (exceptionDetails_) { + Local exception = exceptionDetails_.value()->ToObject(ecmaVm); + result->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "exceptionDetails")), + Local(exception)); + } + + return result; +} + +Local GetPossibleBreakpointsReturns::ToObject(const EcmaVM *ecmaVm) +{ + size_t len = locations_.size(); + Local values = ArrayRef::New(ecmaVm, len); + for (size_t i = 0; i < len; i++) { + Local location = locations_[i]->ToObject(ecmaVm); + values->Set(ecmaVm, i, location); + } + + Local result = NewObject(ecmaVm); + result->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "locations")), values); + + return result; +} + +Local GetScriptSourceReturns::ToObject(const EcmaVM *ecmaVm) +{ + Local result = NewObject(ecmaVm); + + result->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "scriptSource")), + Local(StringRef::NewFromUtf8(ecmaVm, scriptSource_.c_str()))); + if (bytecode_) { + result->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "bytecode")), + Local(StringRef::NewFromUtf8(ecmaVm, bytecode_->c_str()))); + } + + return result; +} + +Local RestartFrameReturns::ToObject(const EcmaVM *ecmaVm) +{ + size_t len = callFrames_.size(); + Local values = ArrayRef::New(ecmaVm, len); + for (size_t i = 0; i < len; i++) { + Local location = callFrames_[i]->ToObject(ecmaVm); + values->Set(ecmaVm, i, location); + } + + Local result = NewObject(ecmaVm); + result->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "callFrames")), values); + return result; +} + +Local SearchInContentReturns::ToObject(const EcmaVM *ecmaVm) +{ + size_t len = result_.size(); + Local values = ArrayRef::New(ecmaVm, len); + for (size_t i = 0; i < len; i++) { + Local location = result_[i]->ToObject(ecmaVm); + values->Set(ecmaVm, i, location); + } + + Local result = NewObject(ecmaVm); + result->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "result")), values); + + return result; +} + +Local SetBreakpointReturns::ToObject(const EcmaVM *ecmaVm) +{ + Local result = NewObject(ecmaVm); + + result->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "breakpointId")), + Local(StringRef::NewFromUtf8(ecmaVm, breakpointId_.c_str()))); + + Local location = location_->ToObject(ecmaVm); + result->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "actualLocation")), Local(location)); + + return result; +} + +Local SetInstrumentationBreakpointReturns::ToObject(const EcmaVM *ecmaVm) +{ + Local result = NewObject(ecmaVm); + + result->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "breakpointId")), + Local(StringRef::NewFromUtf8(ecmaVm, breakpointId_.c_str()))); + + return result; +} + +Local SetScriptSourceReturns::ToObject(const EcmaVM *ecmaVm) +{ + Local result = NewObject(ecmaVm); + + if (callFrames_) { + CVector> callFrame(std::move(callFrames_.value())); + size_t len = callFrame.size(); + Local values = ArrayRef::New(ecmaVm, len); + for (size_t i = 0; i < len; i++) { + Local location = callFrame[i]->ToObject(ecmaVm); + values->Set(ecmaVm, i, location); + } + result->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "callFrames")), values); + } + + if (stackChanged_) { + result->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "stackChanged")), + BooleanRef::New(ecmaVm, stackChanged_.value())); + } + + if (exceptionDetails_) { + result->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "exceptionDetails")), + Local(exceptionDetails_.value()->ToObject(ecmaVm))); + } + + return result; +} + +Local GetPropertiesReturns::ToObject(const EcmaVM *ecmaVm) +{ + Local result = NewObject(ecmaVm); + + size_t len = result_.size(); + Local values = ArrayRef::New(ecmaVm, len); + for (size_t i = 0; i < len; i++) { + Local descriptor = result_[i]->ToObject(ecmaVm); + values->Set(ecmaVm, i, descriptor); + } + result->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "result")), values); + if (internalPropertyDescripties_) { + auto descripties = std::move(internalPropertyDescripties_.value()); + len = descripties.size(); + values = ArrayRef::New(ecmaVm, len); + for (size_t i = 0; i < len; i++) { + Local descriptor = descripties[i]->ToObject(ecmaVm); + values->Set(ecmaVm, i, descriptor); + } + result->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "internalProperties")), values); + } + if (privateProperties_) { + auto descripties = std::move(privateProperties_.value()); + len = descripties.size(); + values = ArrayRef::New(ecmaVm, len); + for (size_t i = 0; i < len; i++) { + Local descriptor = descripties[i]->ToObject(ecmaVm); + values->Set(ecmaVm, i, descriptor); + } + result->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "privateProperties")), values); + } + if (exceptionDetails_) { + result->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "exceptionDetails")), + Local(exceptionDetails_.value()->ToObject(ecmaVm))); + } + + return result; +} +} // namespace panda::tooling::ecmascript \ No newline at end of file diff --git a/ecmascript/tooling/base/pt_returns.h b/ecmascript/tooling/base/pt_returns.h new file mode 100644 index 0000000000000000000000000000000000000000..0fe9d88878d4a616c5e6a3a08aeb5dcbc379e8ea --- /dev/null +++ b/ecmascript/tooling/base/pt_returns.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_RETURNS_H +#define PANDA_TOOLING_ECMASCRIPT_RETURNS_H + +#include "ecmascript/tooling/base/pt_types.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::CString; + +class PtBaseReturns : public PtBaseTypes { +public: + PtBaseReturns() = default; + ~PtBaseReturns() override = default; + + Local ToObject(const EcmaVM *ecmaVm) override + { + return NewObject(ecmaVm); + } + +private: + NO_COPY_SEMANTIC(PtBaseReturns); + NO_MOVE_SEMANTIC(PtBaseReturns); +}; + +class EnableReturns : public PtBaseReturns { +public: + explicit EnableReturns(UniqueDebuggerId id) : debuggerId_(std::move(id)) {} + ~EnableReturns() override = default; + + Local ToObject(const EcmaVM *ecmaVm) override; + +private: + EnableReturns() = default; + NO_COPY_SEMANTIC(EnableReturns); + NO_MOVE_SEMANTIC(EnableReturns); + + UniqueDebuggerId debuggerId_ {}; +}; + +class SetBreakpointByUrlReturns : public PtBaseReturns { +public: + explicit SetBreakpointByUrlReturns(CString id, CVector> locations) + : id_(std::move(id)), locations_(std::move(locations)) + {} + ~SetBreakpointByUrlReturns() override = default; + + Local ToObject(const EcmaVM *ecmaVm) override; + +private: + SetBreakpointByUrlReturns() = default; + NO_COPY_SEMANTIC(SetBreakpointByUrlReturns); + NO_MOVE_SEMANTIC(SetBreakpointByUrlReturns); + + CString id_ {}; + CVector> locations_ {}; +}; + +class EvaluateOnCallFrameReturns : public PtBaseReturns { +public: + explicit EvaluateOnCallFrameReturns(std::unique_ptr result, + std::optional> exceptionDetails = std::nullopt) + : result_(std::move(result)), exceptionDetails_(std::move(exceptionDetails)) + {} + ~EvaluateOnCallFrameReturns() override = default; + Local ToObject(const EcmaVM *ecmaVm) override; + +private: + EvaluateOnCallFrameReturns() = default; + NO_COPY_SEMANTIC(EvaluateOnCallFrameReturns); + NO_MOVE_SEMANTIC(EvaluateOnCallFrameReturns); + + std::unique_ptr result_ {}; + std::optional> exceptionDetails_ {}; +}; + +class GetPossibleBreakpointsReturns : public PtBaseReturns { +public: + explicit GetPossibleBreakpointsReturns(CVector> locations) + : locations_(std::move(locations)) + {} + ~GetPossibleBreakpointsReturns() override = default; + + Local ToObject(const EcmaVM *ecmaVm) override; + +private: + GetPossibleBreakpointsReturns() = default; + NO_COPY_SEMANTIC(GetPossibleBreakpointsReturns); + NO_MOVE_SEMANTIC(GetPossibleBreakpointsReturns); + + CVector> locations_ {}; +}; + +class GetScriptSourceReturns : public PtBaseReturns { +public: + explicit GetScriptSourceReturns(CString scriptSource, std::optional bytecode = std::nullopt) + : scriptSource_(std::move(scriptSource)), bytecode_(std::move(bytecode)) + {} + ~GetScriptSourceReturns() override = default; + + Local ToObject(const EcmaVM *ecmaVm) override; + +private: + GetScriptSourceReturns() = default; + NO_COPY_SEMANTIC(GetScriptSourceReturns); + NO_MOVE_SEMANTIC(GetScriptSourceReturns); + + CString scriptSource_ {}; + std::optional bytecode_ {}; +}; + +class RestartFrameReturns : public PtBaseReturns { +public: + explicit RestartFrameReturns(CVector> callFrames) : callFrames_(std::move(callFrames)) + {} + ~RestartFrameReturns() override = default; + Local ToObject(const EcmaVM *ecmaVm) override; + +private: + RestartFrameReturns() = default; + NO_COPY_SEMANTIC(RestartFrameReturns); + NO_MOVE_SEMANTIC(RestartFrameReturns); + + CVector> callFrames_ {}; +}; + +class SearchInContentReturns : public PtBaseReturns { +public: + explicit SearchInContentReturns(CVector> result) : result_(std::move(result)) + {} + ~SearchInContentReturns() override = default; + Local ToObject(const EcmaVM *ecmaVm) override; + +private: + SearchInContentReturns() = default; + NO_COPY_SEMANTIC(SearchInContentReturns); + NO_MOVE_SEMANTIC(SearchInContentReturns); + + CVector> result_ {}; +}; + +class SetBreakpointReturns : public PtBaseReturns { +public: + explicit SetBreakpointReturns(CString id, std::unique_ptr location) + : breakpointId_(std::move(id)), location_(std::move(location)) + {} + ~SetBreakpointReturns() override = default; + Local ToObject(const EcmaVM *ecmaVm) override; + +private: + SetBreakpointReturns() = default; + NO_COPY_SEMANTIC(SetBreakpointReturns); + NO_MOVE_SEMANTIC(SetBreakpointReturns); + CString breakpointId_ {}; + std::unique_ptr location_ {}; +}; + +class SetInstrumentationBreakpointReturns : public PtBaseReturns { +public: + explicit SetInstrumentationBreakpointReturns(CString id) : breakpointId_(std::move(id)) + {} + ~SetInstrumentationBreakpointReturns() override = default; + Local ToObject(const EcmaVM *ecmaVm) override; + +private: + SetInstrumentationBreakpointReturns() = default; + NO_COPY_SEMANTIC(SetInstrumentationBreakpointReturns); + NO_MOVE_SEMANTIC(SetInstrumentationBreakpointReturns); + + CString breakpointId_ {}; +}; + +class SetScriptSourceReturns : public PtBaseReturns { +public: + explicit SetScriptSourceReturns(std::optional>> callFrames = std::nullopt, + std::optional stackChanged = std::nullopt, + std::optional> exceptionDetails = std::nullopt) + : callFrames_(std::move(callFrames)), + stackChanged_(stackChanged), + exceptionDetails_(std::move(exceptionDetails)) + {} + ~SetScriptSourceReturns() override = default; + Local ToObject(const EcmaVM *ecmaVm) override; + +private: + SetScriptSourceReturns() = default; + NO_COPY_SEMANTIC(SetScriptSourceReturns); + NO_MOVE_SEMANTIC(SetScriptSourceReturns); + + std::optional>> callFrames_ {}; + std::optional stackChanged_ {}; + std::optional> exceptionDetails_ {}; +}; + +class GetPropertiesReturns : public PtBaseReturns { +public: + explicit GetPropertiesReturns(CVector> descriptor, + std::optional>> internalDescripties = std::nullopt, + std::optional>> privateProperties = std::nullopt, + std::optional> exceptionDetails = std::nullopt) + : result_(std::move(descriptor)), + internalPropertyDescripties_(std::move(internalDescripties)), + privateProperties_(std::move(privateProperties)), + exceptionDetails_(std::move(exceptionDetails)) + {} + ~GetPropertiesReturns() override = default; + Local ToObject(const EcmaVM *ecmaVm) override; + +private: + GetPropertiesReturns() = default; + NO_COPY_SEMANTIC(GetPropertiesReturns); + NO_MOVE_SEMANTIC(GetPropertiesReturns); + + CVector> result_ {}; + std::optional>> internalPropertyDescripties_ {}; + std::optional>> privateProperties_ {}; + std::optional> exceptionDetails_ {}; +}; +} // namespace panda::tooling::ecmascript +#endif \ No newline at end of file diff --git a/ecmascript/tooling/base/pt_script.cpp b/ecmascript/tooling/base/pt_script.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5389f6fdd552fcdb0ff7663492de16021546ecc8 --- /dev/null +++ b/ecmascript/tooling/base/pt_script.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/base/pt_script.h" +#include "ecmascript/tooling/interface/debugger_api.h" + +namespace panda::tooling::ecmascript { +PtScript::PtScript(int32_t scriptId, CString fileName, CString url, CString source) + : scriptId_(DebuggerApi::ToCString(scriptId)), + fileName_(std::move(fileName)), + url_(std::move(url)), + scriptSource_(std::move(source)) +{ + endLine_ = std::count(scriptSource_.begin(), scriptSource_.end(), '\n'); +} +} // namespace panda::tooling::ecmascript \ No newline at end of file diff --git a/ecmascript/tooling/base/pt_script.h b/ecmascript/tooling/base/pt_script.h new file mode 100644 index 0000000000000000000000000000000000000000..c64f48151a7ffd785b57ef5cc841fd0264faa8c9 --- /dev/null +++ b/ecmascript/tooling/base/pt_script.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_DEBUGGER_SCRIPT_H +#define PANDA_TOOLING_ECMASCRIPT_DEBUGGER_SCRIPT_H + +#include "libpandabase/macros.h" +#include "ecmascript/mem/c_string.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::CString; + +enum class ScriptMatchType : uint8_t { + SCRIPT_ID, + URL, + FILE_NAME, + HASH, +}; + +class PtScript { +public: + PtScript(int32_t scriptId, CString fileName, CString url, CString source); + ~PtScript() = default; + + CString GetScriptId() const + { + return scriptId_; + } + + void SetScriptId(const CString &scriptId) + { + scriptId_ = scriptId; + } + + CString GetFileName() const + { + return fileName_; + } + + void SetFileName(const CString &fileName) + { + fileName_ = fileName; + } + + CString GetUrl() const + { + return url_; + } + + void SetUrl(const CString &url) + { + url_ = url; + } + + CString GetHash() const + { + return hash_; + } + + void SetHash(const CString &hash) + { + hash_ = hash; + } + + CString GetScriptSource() const + { + return scriptSource_; + } + + void SetScriptSource(const CString &scriptSource) + { + scriptSource_ = scriptSource; + } + + CString GetSourceMapUrl() const + { + return sourceMapUrl_; + } + + void SetSourceMapUrl(const CString &sourceMapUrl) + { + sourceMapUrl_ = sourceMapUrl; + } + + int32_t GetEndLine() const + { + return endLine_; + } + + void SetEndLine(int32_t endLine) + { + endLine_ = endLine; + } + +private: + NO_COPY_SEMANTIC(PtScript); + NO_MOVE_SEMANTIC(PtScript); + + CString scriptId_ {}; // start from 0, such as "0","1","2"... + CString fileName_ {}; // binary file name, such as xx.bin + CString url_ {}; // source file name, such as xx.js + CString hash_ {}; // js source file hash code + CString scriptSource_ {}; // js source code + CString sourceMapUrl_ {}; // source map url + int32_t endLine_ {0}; // total line number of source file +}; +} // namespace panda::tooling::ecmascript +#endif diff --git a/ecmascript/tooling/base/pt_types.cpp b/ecmascript/tooling/base/pt_types.cpp new file mode 100644 index 0000000000000000000000000000000000000000..313a73f779cadd24427f64f2937cabcc7f8df8cd --- /dev/null +++ b/ecmascript/tooling/base/pt_types.cpp @@ -0,0 +1,1538 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pt_types.h" + +namespace panda::tooling::ecmascript { +using ObjectType = RemoteObject::TypeName; +using ObjectSubType = RemoteObject::SubTypeName; +using ObjectClassName = RemoteObject::ClassName; + +const CString ObjectType::Object = "object"; // NOLINT (readability-identifier-naming) +const CString ObjectType::Function = "function"; // NOLINT (readability-identifier-naming) +const CString ObjectType::Undefined = "undefined"; // NOLINT (readability-identifier-naming) +const CString ObjectType::String = "string"; // NOLINT (readability-identifier-naming) +const CString ObjectType::Number = "number"; // NOLINT (readability-identifier-naming) +const CString ObjectType::Boolean = "boolean"; // NOLINT (readability-identifier-naming) +const CString ObjectType::Symbol = "symbol"; // NOLINT (readability-identifier-naming) +const CString ObjectType::Bigint = "bigint"; // NOLINT (readability-identifier-naming) +const CString ObjectType::Wasm = "wasm"; // NOLINT (readability-identifier-naming) + +const CString ObjectSubType::Array = "array"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Null = "null"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Node = "node"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Regexp = "regexp"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Date = "date"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Map = "map"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Set = "set"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Weakmap = "weakmap"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Weakset = "weakset"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Iterator = "iterator"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Generator = "generator"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Error = "error"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Proxy = "proxy"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Promise = "promise"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Typedarray = "typedarray"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Arraybuffer = "arraybuffer"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Dataview = "dataview"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::I32 = "i32"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::I64 = "i64"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::F32 = "f32"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::F64 = "f64"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::V128 = "v128"; // NOLINT (readability-identifier-naming) +const CString ObjectSubType::Externref = "externref"; // NOLINT (readability-identifier-naming) + +const CString ObjectClassName::Object = "Object"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Function = "Function"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Array = "Array"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Regexp = "RegExp"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Date = "Date"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Map = "Map"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Set = "Set"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Weakmap = "Weakmap"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Weakset = "Weakset"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::ArrayIterator = "ArrayIterator"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::StringIterator = "StringIterator"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::SetIterator = "SetIterator"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::MapIterator = "MapIterator"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Iterator = "Iterator"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Error = "Error"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Proxy = "Object"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Promise = "Promise"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Typedarray = "Typedarray"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Arraybuffer = "Arraybuffer"; // NOLINT (readability-identifier-naming) +const CString ObjectClassName::Global = "global"; // NOLINT (readability-identifier-naming) + +const CString RemoteObject::ObjectDescription = "Object"; // NOLINT (readability-identifier-naming) +const CString RemoteObject::GlobalDescription = "global"; // NOLINT (readability-identifier-naming) +const CString RemoteObject::ProxyDescription = "Proxy"; // NOLINT (readability-identifier-naming) +const CString RemoteObject::PromiseDescription = "Promise"; // NOLINT (readability-identifier-naming) +const CString RemoteObject::ArrayIteratorDescription = // NOLINT (readability-identifier-naming) + "ArrayIterator"; +const CString RemoteObject::StringIteratorDescription = // NOLINT (readability-identifier-naming) + "StringIterator"; +const CString RemoteObject::SetIteratorDescription = "SetIterator"; // NOLINT (readability-identifier-naming) +const CString RemoteObject::MapIteratorDescription = "MapIterator"; // NOLINT (readability-identifier-naming) +const CString RemoteObject::WeakMapDescription = "WeakMap"; // NOLINT (readability-identifier-naming) +const CString RemoteObject::WeakSetDescription = "WeakSet"; // NOLINT (readability-identifier-naming) + +static constexpr uint64_t DOUBLE_SIGN_MASK = 0x8000000000000000ULL; + +Local PtBaseTypes::NewObject(const EcmaVM *ecmaVm) +{ + return ObjectRef::New(ecmaVm); +} + +std::unique_ptr RemoteObject::FromTagged(const EcmaVM *ecmaVm, const Local &tagged) +{ + if (tagged->IsNull() || tagged->IsUndefined() || tagged->IsBoolean() || tagged->IsNumber()) { + return std::make_unique(ecmaVm, tagged); + } + if (tagged->IsString()) { + return std::make_unique(ecmaVm, tagged); + } + if (tagged->IsSymbol()) { + return std::make_unique(ecmaVm, tagged); + } + if (tagged->IsFunction()) { + return std::make_unique(ecmaVm, tagged); + } + if (tagged->IsArray(ecmaVm)) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Array, ObjectSubType::Array); + } + if (tagged->IsRegExp()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Regexp, ObjectSubType::Regexp); + } + if (tagged->IsDate()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Date, ObjectSubType::Date); + } + if (tagged->IsMap()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Map, ObjectSubType::Map); + } + if (tagged->IsWeakMap()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Weakmap, ObjectSubType::Weakmap); + } + if (tagged->IsSet()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Set, ObjectSubType::Set); + } + if (tagged->IsWeakSet()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Weakset, ObjectSubType::Weakset); + } + if (tagged->IsError()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Error, ObjectSubType::Error); + } + if (tagged->IsProxy()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Proxy, ObjectSubType::Proxy); + } + if (tagged->IsPromise()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Promise, ObjectSubType::Promise); + } + if (tagged->IsArrayBuffer()) { + return std::make_unique( + ecmaVm, tagged, ObjectClassName::Arraybuffer, ObjectSubType::Arraybuffer); + } + if (tagged->IsArrayIterator()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::ArrayIterator); + } + if (tagged->IsStringIterator()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::StringIterator); + } + if (tagged->IsSetIterator()) { + return std::make_unique( + ecmaVm, tagged, ObjectClassName::SetIterator, ObjectSubType::Iterator); + } + if (tagged->IsMapIterator()) { + return std::make_unique( + ecmaVm, tagged, ObjectClassName::MapIterator, ObjectSubType::Iterator); + } + if (tagged->IsObject()) { + return std::make_unique(ecmaVm, tagged, ObjectClassName::Object); + } + std::unique_ptr object = std::make_unique(); + object->SetType(ObjectType::Undefined); + return object; +} + +PrimitiveRemoteObject::PrimitiveRemoteObject(const EcmaVM *ecmaVm, const Local &tagged) +{ + if (tagged->IsNull()) { + this->SetType(ObjectType::Object).SetSubType(ObjectSubType::Null).SetValue(tagged); + } else if (tagged->IsBoolean()) { + this->SetType(ObjectType::Boolean).SetValue(tagged); + } else if (tagged->IsUndefined()) { + this->SetType(ObjectType::Undefined); + } else if (tagged->IsNumber()) { + this->SetType(ObjectType::Number) + .SetDescription(DebuggerApi::ConvertToString(tagged->ToString(ecmaVm)->ToString())); + double val = Local(tagged)->Value(); + if (!std::isfinite(val) || (val == 0 && ((bit_cast(val) & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK))) { + this->SetUnserializableValue(this->GetDescription()); + } else { + this->SetValue(tagged); + } + } +} + +CString ObjectRemoteObject::DescriptionForObject(const EcmaVM *ecmaVm, const Local &tagged) +{ + if (tagged->IsArray(ecmaVm)) { + return DescriptionForArray(ecmaVm, Local(tagged)); + } + if (tagged->IsRegExp()) { + return DescriptionForRegexp(ecmaVm, Local(tagged)); + } + if (tagged->IsDate()) { + return DescriptionForDate(ecmaVm, Local(tagged)); + } + if (tagged->IsMap()) { + return DescriptionForMap(Local(tagged)); + } + if (tagged->IsWeakMap()) { + return RemoteObject::WeakMapDescription; + } + if (tagged->IsSet()) { + return DescriptionForSet(Local(tagged)); + } + if (tagged->IsWeakSet()) { + return RemoteObject::WeakSetDescription; + } + if (tagged->IsError()) { + return DescriptionForError(ecmaVm, tagged); + } + if (tagged->IsProxy()) { + return RemoteObject::ProxyDescription; + } + if (tagged->IsPromise()) { + return RemoteObject::PromiseDescription; + } + if (tagged->IsArrayIterator()) { + return RemoteObject::ArrayIteratorDescription; + } + if (tagged->IsStringIterator()) { + return RemoteObject::StringIteratorDescription; + } + if (tagged->IsSetIterator()) { + return RemoteObject::SetIteratorDescription; + } + if (tagged->IsMapIterator()) { + return RemoteObject::MapIteratorDescription; + } + if (tagged->IsArrayBuffer()) { + return DescriptionForArrayBuffer(ecmaVm, Local(tagged)); + } + return RemoteObject::ObjectDescription; +} + +CString ObjectRemoteObject::DescriptionForArray(const EcmaVM *ecmaVm, const Local &tagged) +{ + CString description = "Array(" + DebuggerApi::ToCString(tagged->Length(ecmaVm)) + ")"; + return description; +} + +CString ObjectRemoteObject::DescriptionForRegexp(const EcmaVM *ecmaVm, const Local &tagged) +{ + CString regexpSource = DebuggerApi::ConvertToString(tagged->GetOriginalSource(ecmaVm)->ToString()); + CString description = "/" + regexpSource + "/"; + return description; +} + +CString ObjectRemoteObject::DescriptionForDate(const EcmaVM *ecmaVm, const Local &tagged) +{ + CString description = DebuggerApi::ConvertToString(tagged->ToString(ecmaVm)->ToString()); + return description; +} + +CString ObjectRemoteObject::DescriptionForMap(const Local &tagged) +{ + CString description = ("Map(" + DebuggerApi::ToCString(tagged->GetSize()) + ")"); + return description; +} + +CString ObjectRemoteObject::DescriptionForSet(const Local &tagged) +{ + CString description = ("Set(" + DebuggerApi::ToCString(tagged->GetSize()) + ")"); + return description; +} + +CString ObjectRemoteObject::DescriptionForError(const EcmaVM *ecmaVm, const Local &tagged) +{ + Local stack = StringRef::NewFromUtf8(ecmaVm, "stack"); + Local result = Local(tagged)->Get(ecmaVm, stack); + return DebuggerApi::ConvertToString(result->ToString(ecmaVm)->ToString()); +} + +CString ObjectRemoteObject::DescriptionForArrayBuffer(const EcmaVM *ecmaVm, const Local &tagged) +{ + int32_t len = tagged->ByteLength(ecmaVm); + CString description = ("ArrayBuffer(" + DebuggerApi::ToCString(len) + ")"); + return description; +} + +CString SymbolRemoteObject::DescriptionForSymbol(const EcmaVM *ecmaVm, const Local &tagged) const +{ + CString description = + "Symbol(" + DebuggerApi::ConvertToString(Local(tagged->GetDescription(ecmaVm))->ToString()) + ")"; + return description; +} + +CString FunctionRemoteObject::DescriptionForFunction(const EcmaVM *ecmaVm, const Local &tagged) const +{ + CString sourceCode; + if (tagged->IsNative(ecmaVm)) { + sourceCode = "[native code]"; + } else { + sourceCode = "[js code]"; + } + Local name = tagged->GetName(ecmaVm); + CString description = "function " + DebuggerApi::ConvertToString(name->ToString()) + "() { " + sourceCode + " }"; + return description; +} + +std::unique_ptr RemoteObject::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty() || !params->IsObject()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create params is nullptr"; + return nullptr; + } + CString error; + auto remoteObject = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "type"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + auto type = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + if (ObjectType::Valid(type)) { + remoteObject->type_ = type; + } else { + error += "'type' is invalid;"; + } + } else { + error += "'type' should be a String;"; + } + } else { + error += "should contain 'type';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "subtype"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + auto type = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + if (ObjectSubType::Valid(type)) { + remoteObject->subtype_ = type; + } else { + error += "'subtype' is invalid;"; + } + } else { + error += "'subtype' should be a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "className"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + remoteObject->className_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'className' should be a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "value"))); + if (!result.IsEmpty()) { + remoteObject->value_ = result; + } + result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "unserializableValue"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + remoteObject->unserializableValue_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'unserializableValue' should be a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "description"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + remoteObject->description_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'description' should be a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "objectId"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + remoteObject->objectId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'objectId' should be a String;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "RemoteObject::Create " << error; + return nullptr; + } + + return remoteObject; +} + +Local RemoteObject::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "type")), + Local(StringRef::NewFromUtf8(ecmaVm, type_.c_str()))); + if (subtype_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "subtype")), + Local(StringRef::NewFromUtf8(ecmaVm, subtype_->c_str()))); + } + if (className_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "className")), + Local(StringRef::NewFromUtf8(ecmaVm, className_->c_str()))); + } + if (value_) { + params->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "value")), value_.value()); + } + if (unserializableValue_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "unserializableValue")), + Local(StringRef::NewFromUtf8(ecmaVm, unserializableValue_->c_str()))); + } + if (description_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "description")), + Local(StringRef::NewFromUtf8(ecmaVm, description_->c_str()))); + } + if (objectId_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "objectId")), + Local(StringRef::NewFromUtf8(ecmaVm, objectId_->c_str()))); + } + + return params; +} + +std::unique_ptr ExceptionDetails::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty() || !params->IsObject()) { + LOG(ERROR, DEBUGGER) << "ExceptionDetails::Create params is nullptr"; + return nullptr; + } + CString error; + auto exceptionDetails = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "exceptionId"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + exceptionDetails->exceptionId_ = static_cast(Local(result)->Value()); + } else { + error += "'exceptionId' should be a Number;"; + } + } else { + error += "should contain 'exceptionId';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "text"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + exceptionDetails->text_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'text' should be a String;"; + } + } else { + error += "should contain 'text';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "lineNumber"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + exceptionDetails->line_ = static_cast(Local(result)->Value()); + } else { + error += "'lineNumber' should be a Number;"; + } + } else { + error += "should contain 'lineNumber';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "columnNumber"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + exceptionDetails->column_ = static_cast(Local(result)->Value()); + } else { + error += "'columnNumber' should be a Number;"; + } + } else { + error += "should contain 'columnNumber';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "scriptId"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + exceptionDetails->scriptId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'scriptId' should be a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "url"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + exceptionDetails->url_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'url' should be a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "exception"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = RemoteObject::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'exception' format error;"; + } else { + exceptionDetails->exception_ = std::move(obj); + } + } else { + error += "'exception' should be an Object;"; + } + } + + result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "executionContextId"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + exceptionDetails->executionContextId_ = static_cast(Local(result)->Value()); + } else { + error += "'executionContextId' should be a Number;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "ExceptionDetails::Create " << error; + return nullptr; + } + + return exceptionDetails; +} + +Local ExceptionDetails::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "exceptionId")), + IntegerRef::New(ecmaVm, exceptionId_)); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "text")), + Local(StringRef::NewFromUtf8(ecmaVm, text_.c_str()))); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "lineNumber")), IntegerRef::New(ecmaVm, line_)); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "columnNumber")), IntegerRef::New(ecmaVm, column_)); + if (scriptId_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "scriptId")), + Local(StringRef::NewFromUtf8(ecmaVm, scriptId_->c_str()))); + } + if (url_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "url")), + Local(StringRef::NewFromUtf8(ecmaVm, url_->c_str()))); + } + if (exception_) { + ASSERT(exception_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "exception")), + Local(exception_.value()->ToObject(ecmaVm))); + } + if (executionContextId_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "executionContextId")), + IntegerRef::New(ecmaVm, executionContextId_.value())); + } + + return params; +} + +std::unique_ptr InternalPropertyDescriptor::Create( + const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty() || !params->IsObject()) { + LOG(ERROR, DEBUGGER) << "InternalPropertyDescriptor::Create params is nullptr"; + return nullptr; + } + CString error; + auto internalPropertyDescriptor = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "name"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + internalPropertyDescriptor->name_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'name' should be a String;"; + } + } else { + error += "should contain 'name';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "value"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = RemoteObject::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'value' format error;"; + } else { + internalPropertyDescriptor->value_ = std::move(obj); + } + } else { + error += "'value' should be an Object;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "InternalPropertyDescriptor::Create " << error; + return nullptr; + } + + return internalPropertyDescriptor; +} + +Local InternalPropertyDescriptor::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "name")), + Local(StringRef::NewFromUtf8(ecmaVm, name_.c_str()))); + if (value_) { + ASSERT(value_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "value")), + Local(value_.value()->ToObject(ecmaVm))); + } + + return params; +} + +std::unique_ptr PrivatePropertyDescriptor::Create( + const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty() || !params->IsObject()) { + LOG(ERROR, DEBUGGER) << "PrivatePropertyDescriptor::Create params is nullptr"; + return nullptr; + } + CString error; + auto propertyDescriptor = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "name"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + propertyDescriptor->name_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'name' should be a String;"; + } + } else { + error += "should contain 'name';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "value"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = RemoteObject::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'value' format error;"; + } else { + propertyDescriptor->value_ = std::move(obj); + } + } else { + error += "'value' should be a Object;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "get"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = RemoteObject::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'get' format error;"; + } else { + propertyDescriptor->get_ = std::move(obj); + } + } else { + error += "'get' should be an Object;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "set"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = RemoteObject::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'set' format error;"; + } else { + propertyDescriptor->set_ = std::move(obj); + } + } else { + error += "'set' should be an Object;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "PrivatePropertyDescriptor::Create " << error; + return nullptr; + } + + return propertyDescriptor; +} + +Local PrivatePropertyDescriptor::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "name")), + Local(StringRef::NewFromUtf8(ecmaVm, name_.c_str()))); + if (value_) { + ASSERT(value_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "value")), + Local(value_.value()->ToObject(ecmaVm))); + } + if (get_) { + ASSERT(get_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "get")), + Local(get_.value()->ToObject(ecmaVm))); + } + if (set_) { + ASSERT(set_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "set")), + Local(set_.value()->ToObject(ecmaVm))); + } + + return params; +} + +std::unique_ptr PropertyDescriptor::FromProperty( + const EcmaVM *ecmaVm, const Local &name, const PropertyAttribute &property) +{ + std::unique_ptr debuggerProperty = std::make_unique(); + + CString nameStr; + if (name->IsSymbol()) { + Local symbol(name); + nameStr = + "Symbol(" + DebuggerApi::ConvertToString(Local(name)->GetDescription(ecmaVm)->ToString()) + ")"; + debuggerProperty->symbol_ = RemoteObject::FromTagged(ecmaVm, name); + } else { + nameStr = DebuggerApi::ConvertToString(name->ToString(ecmaVm)->ToString()); + } + + debuggerProperty->name_ = nameStr; + if (property.HasValue()) { + debuggerProperty->value_ = RemoteObject::FromTagged(ecmaVm, property.GetValue(ecmaVm)); + } + if (property.HasWritable()) { + debuggerProperty->writable_ = property.IsWritable(); + } + if (property.HasGetter()) { + debuggerProperty->get_ = RemoteObject::FromTagged(ecmaVm, property.GetGetter(ecmaVm)); + } + if (property.HasSetter()) { + debuggerProperty->set_ = RemoteObject::FromTagged(ecmaVm, property.GetSetter(ecmaVm)); + } + debuggerProperty->configurable_ = property.IsConfigurable(); + debuggerProperty->enumerable_ = property.IsEnumerable(); + debuggerProperty->isOwn_ = true; + + return debuggerProperty; +} + +std::unique_ptr PropertyDescriptor::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty() || !params->IsObject()) { + LOG(ERROR, DEBUGGER) << "PropertyDescriptor::Create params is nullptr"; + return nullptr; + } + CString error; + auto propertyDescriptor = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "name"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + propertyDescriptor->name_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'name' should be a String;"; + } + } else { + error += "should contain 'name';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "value"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = RemoteObject::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'value' format error;"; + } else { + propertyDescriptor->value_ = std::move(obj); + } + } else { + error += "'value' should be an Object;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "writable"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + propertyDescriptor->writable_ = result->IsTrue(); + } else { + error += "'writable' should be a Boolean;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "get"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = RemoteObject::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'get' format error;"; + } else { + propertyDescriptor->get_ = std::move(obj); + } + } else { + error += "'get' should be an Object;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "set"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = RemoteObject::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'set' format error;"; + } else { + propertyDescriptor->set_ = std::move(obj); + } + } else { + error += "'set' should be an Object;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "configurable"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + propertyDescriptor->configurable_ = result->IsTrue(); + } else { + error += "'configurable' should be a Boolean;"; + } + } else { + error += "should contain 'configurable';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "enumerable"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + propertyDescriptor->enumerable_ = result->IsTrue(); + } else { + error += "'enumerable' should be a Boolean;"; + } + } else { + error += "should contain 'enumerable';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "wasThrown"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + propertyDescriptor->wasThrown_ = result->IsTrue(); + } else { + error += "'wasThrown' should be a Boolean;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "isOwn"))); + if (!result.IsEmpty()) { + if (result->IsBoolean()) { + propertyDescriptor->isOwn_ = result->IsTrue(); + } else { + error += "'isOwn' should be a Boolean;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "symbol"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = RemoteObject::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'symbol' format error;"; + } else { + propertyDescriptor->symbol_ = std::move(obj); + } + } else { + error += "'symbol' should be an Object;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "PropertyDescriptor::Create " << error; + return nullptr; + } + + return propertyDescriptor; +} + +Local PropertyDescriptor::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "name")), + Local(StringRef::NewFromUtf8(ecmaVm, name_.c_str()))); + if (value_) { + ASSERT(value_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "value")), + Local(value_.value()->ToObject(ecmaVm))); + } + if (writable_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "writable")), + BooleanRef::New(ecmaVm, writable_.value())); + } + if (get_) { + ASSERT(get_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "get")), + Local(get_.value()->ToObject(ecmaVm))); + } + if (set_) { + ASSERT(set_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "set")), + Local(set_.value()->ToObject(ecmaVm))); + } + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "configurable")), + BooleanRef::New(ecmaVm, configurable_)); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "enumerable")), BooleanRef::New(ecmaVm, enumerable_)); + if (wasThrown_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "wasThrown")), + BooleanRef::New(ecmaVm, wasThrown_.value())); + } + if (isOwn_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "isOwn")), + BooleanRef::New(ecmaVm, isOwn_.value())); + } + if (symbol_) { + ASSERT(symbol_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "symbol")), + Local(symbol_.value()->ToObject(ecmaVm))); + } + + return params; +} + +std::unique_ptr Location::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty() || !params->IsObject()) { + LOG(ERROR, DEBUGGER) << "Location::Create params is nullptr"; + return nullptr; + } + CString error; + auto location = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "scriptId"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + location->scriptId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'scriptId' should be a String;"; + } + } else { + error += "should contain 'scriptId';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "lineNumber"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + location->line_ = static_cast(Local(result)->Value()); + } else { + error += "'lineNumber' should be a Number;"; + } + } else { + error += "should contain 'lineNumber';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "columnNumber"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + location->column_ = static_cast(Local(result)->Value()); + } else { + error += "'columnNumber' should be a Number;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "Location::Create " << error; + return nullptr; + } + + return location; +} + +Local Location::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "scriptId")), + Local(StringRef::NewFromUtf8(ecmaVm, scriptId_.c_str()))); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "lineNumber")), IntegerRef::New(ecmaVm, line_)); + if (column_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "columnNumber")), + IntegerRef::New(ecmaVm, column_.value())); + } + + return params; +} + +std::unique_ptr ScriptPosition::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty() || !params->IsObject()) { + LOG(ERROR, DEBUGGER) << "ScriptPosition::Create params is nullptr"; + return nullptr; + } + CString error; + auto scriptPosition = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "lineNumber"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptPosition->line_ = static_cast(Local(result)->Value()); + } else { + error += "'lineNumber' should be a Number;"; + } + } else { + error += "should contain 'lineNumber';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "columnNumber"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + scriptPosition->column_ = static_cast(Local(result)->Value()); + } else { + error += "'columnNumber' should be a Number;"; + } + } else { + error += "should contain 'columnNumber';"; + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "ScriptPosition::Create " << error; + return nullptr; + } + + return scriptPosition; +} + +Local ScriptPosition::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "lineNumber")), IntegerRef::New(ecmaVm, line_)); + + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "columnNumber")), IntegerRef::New(ecmaVm, column_)); + + return params; +} + +std::unique_ptr SearchMatch::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty() || !params->IsObject()) { + LOG(ERROR, DEBUGGER) << "SearchMatch::Create params is nullptr"; + return nullptr; + } + CString error; + auto locationSearch = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "lineNumber"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + locationSearch->lineNumber_ = static_cast(Local(result)->Value()); + } else { + error += "'lineNumber' should be a Number;"; + } + } else { + error += "should contain 'lineNumber';"; + } + + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "lineContent"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + locationSearch->lineContent_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'lineContent' should be a String;"; + } + } else { + error += "should contain 'lineContent';"; + } + + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "SearchMatch::Create " << error; + return nullptr; + } + + return locationSearch; +} + +Local SearchMatch::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "lineNumber")), IntegerRef::New(ecmaVm, lineNumber_)); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "lineContent")), + Local(StringRef::NewFromUtf8(ecmaVm, lineContent_.c_str()))); + return params; +} + +std::unique_ptr LocationRange::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty() || !params->IsObject()) { + LOG(ERROR, DEBUGGER) << "BreakLocation::Create params is nullptr"; + return nullptr; + } + CString error; + auto locationRange = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "scriptId"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + locationRange->scriptId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'scriptId' should be a String;"; + } + } else { + error += "should contain 'scriptId';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "start"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = ScriptPosition::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'start' format error;"; + } else { + locationRange->start_ = std::move(obj); + } + } else { + error += "'start' should be an Object;"; + } + } else { + error += "should contain 'start';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "end"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = ScriptPosition::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'end' format error;"; + } else { + locationRange->end_ = std::move(obj); + } + } else { + error += "'end' should be an Object;"; + } + } else { + error += "should contain 'end';"; + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "LocationRange::Create " << error; + return nullptr; + } + + return locationRange; +} + +Local LocationRange::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "scriptId")), + Local(StringRef::NewFromUtf8(ecmaVm, scriptId_.c_str()))); + ASSERT(start_ != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "object")), + Local(start_->ToObject(ecmaVm))); + ASSERT(end_ != nullptr); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "object")), Local(end_->ToObject(ecmaVm))); + + return params; +} + +std::unique_ptr BreakLocation::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty() || !params->IsObject()) { + LOG(ERROR, DEBUGGER) << "BreakLocation::Create params is nullptr"; + return nullptr; + } + CString error; + auto breakLocation = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "scriptId"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + breakLocation->scriptId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'scriptId' should be a String;"; + } + } else { + error += "should contain 'scriptId';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "lineNumber"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + breakLocation->line_ = static_cast(Local(result)->Value()); + } else { + error += "'lineNumber' should be a Number;"; + } + } else { + error += "should contain 'lineNumber';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "columnNumber"))); + if (!result.IsEmpty()) { + if (result->IsNumber()) { + breakLocation->column_ = static_cast(Local(result)->Value()); + } else { + error += "'columnNumber' should be a Number;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "type"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + auto type = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + if (BreakType::Valid(type)) { + breakLocation->type_ = type; + } else { + error += "'type' is invalid;"; + } + } else { + error += "'type' should be a String;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "Location::Create " << error; + return nullptr; + } + + return breakLocation; +} + +Local BreakLocation::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "scriptId")), + Local(StringRef::NewFromUtf8(ecmaVm, scriptId_.c_str()))); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "lineNumber")), IntegerRef::New(ecmaVm, line_)); + if (column_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "columnNumber")), + IntegerRef::New(ecmaVm, column_.value())); + } + if (type_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "type")), + Local(StringRef::NewFromUtf8(ecmaVm, type_->c_str()))); + } + + return params; +} + +std::unique_ptr Scope::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty() || !params->IsObject()) { + LOG(ERROR, DEBUGGER) << "Scope::Create params is nullptr"; + return nullptr; + } + CString error; + auto scope = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "type"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + auto type = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + if (Scope::Type::Valid(type)) { + scope->type_ = type; + } else { + error += "'type' is invalid;"; + } + } else { + error += "'type' should be a String;"; + } + } else { + error += "should contain 'type';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "object"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = RemoteObject::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'object' format error;"; + } else { + scope->object_ = std::move(obj); + } + } else { + error += "'object' should be an Object;"; + } + } else { + error += "should contain 'object';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "name"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + scope->name_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'name' should be a String;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "startLocation"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = Location::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'startLocation' format error;"; + } else { + scope->startLocation_ = std::move(obj); + } + } else { + error += "'startLocation' should be an Object;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "endLocation"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = Location::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'endLocation' format error;"; + } else { + scope->endLocation_ = std::move(obj); + } + } else { + error += "'endLocation' should be an Object;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "Location::Create " << error; + return nullptr; + } + + return scope; +} + +Local Scope::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "type")), + Local(StringRef::NewFromUtf8(ecmaVm, type_.c_str()))); + ASSERT(object_ != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "object")), + Local(object_->ToObject(ecmaVm))); + if (name_) { + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "name")), + Local(StringRef::NewFromUtf8(ecmaVm, name_->c_str()))); + } + if (startLocation_) { + ASSERT(startLocation_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "startLocation")), + Local(startLocation_.value()->ToObject(ecmaVm))); + } + if (endLocation_) { + ASSERT(endLocation_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "endLocation")), + Local(endLocation_.value()->ToObject(ecmaVm))); + } + + return params; +} + +std::unique_ptr CallFrame::Create(const EcmaVM *ecmaVm, const Local ¶ms) +{ + if (params.IsEmpty() || !params->IsObject()) { + LOG(ERROR, DEBUGGER) << "CallFrame::Create params is nullptr"; + return nullptr; + } + CString error; + auto callFrame = std::make_unique(); + + Local result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "callFrameId"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + callFrame->callFrameId_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'callFrameId' should be a String;"; + } + } else { + error += "should contain 'callFrameId';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "functionName"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + callFrame->functionName_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'functionName' should be a String;"; + } + } else { + error += "should contain 'functionName';"; + } + result = + Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "functionLocation"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = Location::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'functionLocation' format error;"; + } else { + callFrame->functionLocation_ = std::move(obj); + } + } else { + error += "'functionLocation' should be an Object;"; + } + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "location"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = Location::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'location' format error;"; + } else { + callFrame->location_ = std::move(obj); + } + } else { + error += "'location' should be an Object;"; + } + } else { + error += "should contain 'location';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "url"))); + if (!result.IsEmpty()) { + if (result->IsString()) { + callFrame->url_ = DebuggerApi::ConvertToString(StringRef::Cast(*result)->ToString()); + } else { + error += "'url' should be a String;"; + } + } else { + error += "should contain 'url';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "scopeChain"))); + if (!result.IsEmpty()) { + if (result->IsArray(ecmaVm)) { + auto array = Local(result); + uint32_t len = array->Length(ecmaVm); + Local key = JSValueRef::Undefined(ecmaVm); + for (uint32_t i = 0; i < len; ++i) { + key = IntegerRef::New(ecmaVm, i); + Local result = Local(array)->Get(ecmaVm, key->ToString(ecmaVm)); + std::unique_ptr scope = Scope::Create(ecmaVm, result); + if (result.IsEmpty() || scope == nullptr) { + error += "'scopeChain' format invalid;"; + } + callFrame->scopeChain_.emplace_back(std::move(scope)); + } + } else { + error += "'scopeChain' should be an Array;"; + } + } else { + error += "should contain 'scopeChain';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "this"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = RemoteObject::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'this' format error;"; + } else { + callFrame->this_ = std::move(obj); + } + } else { + error += "'this' should be an Object;"; + } + } else { + error += "should contain 'this';"; + } + result = Local(params)->Get(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "returnValue"))); + if (!result.IsEmpty()) { + if (result->IsObject()) { + std::unique_ptr obj = RemoteObject::Create(ecmaVm, result); + if (obj == nullptr) { + error += "'returnValue' format error;"; + } else { + callFrame->returnValue_ = std::move(obj); + } + } else { + error += "'returnValue' should be an Object;"; + } + } + if (!error.empty()) { + LOG(ERROR, DEBUGGER) << "CallFrame::Create " << error; + return nullptr; + } + + return callFrame; +} + +Local CallFrame::ToObject(const EcmaVM *ecmaVm) +{ + Local params = NewObject(ecmaVm); + + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "callFrameId")), + Local(StringRef::NewFromUtf8(ecmaVm, callFrameId_.c_str()))); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "functionName")), + Local(StringRef::NewFromUtf8(ecmaVm, functionName_.c_str()))); + if (functionLocation_) { + ASSERT(functionLocation_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "functionLocation")), + Local(functionLocation_.value()->ToObject(ecmaVm))); + } + ASSERT(location_ != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "location")), + Local(location_->ToObject(ecmaVm))); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "url")), + Local(StringRef::NewFromUtf8(ecmaVm, url_.c_str()))); + size_t len = scopeChain_.size(); + Local values = ArrayRef::New(ecmaVm, len); + for (size_t i = 0; i < len; i++) { + Local scope = scopeChain_[i]->ToObject(ecmaVm); + values->Set(ecmaVm, i, scope); + } + params->Set(ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "scopeChain")), values); + ASSERT(this_ != nullptr); + params->Set( + ecmaVm, Local(StringRef::NewFromUtf8(ecmaVm, "this")), Local(this_->ToObject(ecmaVm))); + if (returnValue_) { + ASSERT(returnValue_.value() != nullptr); + params->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "returnValue")), + Local(returnValue_.value()->ToObject(ecmaVm))); + } + + return params; +} +} // namespace panda::tooling::ecmascript diff --git a/ecmascript/tooling/base/pt_types.h b/ecmascript/tooling/base/pt_types.h new file mode 100644 index 0000000000000000000000000000000000000000..f0f6c9e48db2019031fafc31fa9dbdbfe3888146 --- /dev/null +++ b/ecmascript/tooling/base/pt_types.h @@ -0,0 +1,1423 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_TYPES_H +#define PANDA_TOOLING_ECMASCRIPT_TYPES_H + +#include +#include + +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/mem/c_string.h" +#include "ecmascript/tooling/interface/debugger_api.h" +#include "libpandabase/macros.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::CList; +using panda::ecmascript::CMap; +using panda::ecmascript::CQueue; +using panda::ecmascript::CString; +using panda::ecmascript::CVector; + +// ========== Base types begin +class PtBaseTypes { +public: + PtBaseTypes() = default; + virtual ~PtBaseTypes() = default; + virtual Local ToObject(const EcmaVM *ecmaVm) = 0; + +protected: + static Local NewObject(const EcmaVM *ecmaVm); + +private: + NO_COPY_SEMANTIC(PtBaseTypes); + NO_MOVE_SEMANTIC(PtBaseTypes); + + friend class ProtocolHandler; + friend class JSBackend; +}; + +// ========== Debugger types begin +// Debugger.BreakpointId +using BreakpointId = CString; +struct BreakpointDetails { + static BreakpointId ToString(const BreakpointDetails &metaData) + { + return "id:" + DebuggerApi::ToCString(metaData.line_) + ":" + DebuggerApi::ToCString(metaData.column_) + ":" + + metaData.url_; + } + + static bool ParseBreakpointId(const BreakpointId &id, BreakpointDetails *metaData) + { + auto lineStart = id.find(':'); + if (lineStart == CString::npos) { + return false; + } + auto columnStart = id.find(':', lineStart + 1); + if (columnStart == CString::npos) { + return false; + } + auto urlStart = id.find(':', columnStart + 1); + if (urlStart == CString::npos) { + return false; + } + CString lineStr = id.substr(lineStart + 1, columnStart - lineStart - 1); + CString columnStr = id.substr(columnStart + 1, urlStart - columnStart - 1); + CString url = id.substr(urlStart + 1); + metaData->line_ = DebuggerApi::CStringToULL(lineStr); + metaData->column_ = DebuggerApi::CStringToULL(columnStr); + metaData->url_ = url; + + return true; + } + + int32_t line_ {0}; + int32_t column_ {0}; + CString url_ {}; +}; + +// Debugger.CallFrameId +using CallFrameId = CString; + +// ========== Runtime types begin +// Runtime.ScriptId +using ScriptId = CString; + +// Runtime.RemoteObjectId +using RemoteObjectId = CString; + +// Runtime.ExecutionContextId +using ExecutionContextId = int32_t; + +// Runtime.UnserializableValue +using UnserializableValue = CString; + +// Runtime.UniqueDebuggerId +using UniqueDebuggerId = CString; + +// Runtime.RemoteObject +class RemoteObject : public PtBaseTypes { +public: + RemoteObject() = default; + virtual ~RemoteObject() override = default; + + static std::unique_ptr FromTagged(const EcmaVM *ecmaVm, const Local &tagged); + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + /* + * @see {#ObjectType} + */ + CString GetType() const + { + return type_; + } + + RemoteObject &SetType(const CString &type) + { + type_ = type; + return *this; + } + /* + * @see {#ObjectSubType} + */ + CString GetSubType() const + { + return subtype_.value_or(""); + } + + RemoteObject &SetSubType(const CString &type) + { + subtype_ = type; + return *this; + } + + bool HasSubType() const + { + return subtype_.has_value(); + } + + CString GetClassName() const + { + return className_.value_or(""); + } + + RemoteObject &SetClassName(const CString &className) + { + className_ = className; + return *this; + } + + bool HasClassName() const + { + return className_.has_value(); + } + + Local GetValue() const + { + return value_.value_or(Local()); + } + + RemoteObject &SetValue(const Local &value) + { + value_ = value; + return *this; + } + + bool HasValue() const + { + return value_.has_value(); + } + + UnserializableValue GetUnserializableValue() const + { + return unserializableValue_.value_or(""); + } + + RemoteObject &SetUnserializableValue(const UnserializableValue &unserializableValue) + { + unserializableValue_ = unserializableValue; + return *this; + } + + bool HasUnserializableValue() const + { + return unserializableValue_.has_value(); + } + + CString GetDescription() const + { + return description_.value_or(""); + } + + RemoteObject &SetDescription(const CString &description) + { + description_ = description; + return *this; + } + + bool HasDescription() const + { + return description_.has_value(); + } + + RemoteObjectId GetObjectId() const + { + return objectId_.value_or(""); + } + + RemoteObject &SetObjectId(const RemoteObjectId &objectId) + { + objectId_ = objectId; + return *this; + } + + RemoteObject &SetObjectId(uint32_t objectId) + { + objectId_ = DebuggerApi::ToCString(objectId); + return *this; + } + + bool HasObjectId() const + { + return objectId_.has_value(); + } + + struct TypeName { + static const CString Object; // NOLINT (readability-identifier-naming) + static const CString Function; // NOLINT (readability-identifier-naming) + static const CString Undefined; // NOLINT (readability-identifier-naming) + static const CString String; // NOLINT (readability-identifier-naming) + static const CString Number; // NOLINT (readability-identifier-naming) + static const CString Boolean; // NOLINT (readability-identifier-naming) + static const CString Symbol; // NOLINT (readability-identifier-naming) + static const CString Bigint; // NOLINT (readability-identifier-naming) + static const CString Wasm; // NOLINT (readability-identifier-naming) + static bool Valid(const CString &type) + { + return type == Object || type == Function || type == Undefined || type == String || type == Number || + type == Boolean || type == Symbol || type == Bigint || type == Wasm; + } + }; + + struct SubTypeName { + static const CString Array; // NOLINT (readability-identifier-naming) + static const CString Null; // NOLINT (readability-identifier-naming) + static const CString Node; // NOLINT (readability-identifier-naming) + static const CString Regexp; // NOLINT (readability-identifier-naming) + static const CString Date; // NOLINT (readability-identifier-naming) + static const CString Map; // NOLINT (readability-identifier-naming) + static const CString Set; // NOLINT (readability-identifier-naming) + static const CString Weakmap; // NOLINT (readability-identifier-naming) + static const CString Weakset; // NOLINT (readability-identifier-naming) + static const CString Iterator; // NOLINT (readability-identifier-naming) + static const CString Generator; // NOLINT (readability-identifier-naming) + static const CString Error; // NOLINT (readability-identifier-naming) + static const CString Proxy; // NOLINT (readability-identifier-naming) + static const CString Promise; // NOLINT (readability-identifier-naming) + static const CString Typedarray; // NOLINT (readability-identifier-naming) + static const CString Arraybuffer; // NOLINT (readability-identifier-naming) + static const CString Dataview; // NOLINT (readability-identifier-naming) + static const CString I32; // NOLINT (readability-identifier-naming) + static const CString I64; // NOLINT (readability-identifier-naming) + static const CString F32; // NOLINT (readability-identifier-naming) + static const CString F64; // NOLINT (readability-identifier-naming) + static const CString V128; // NOLINT (readability-identifier-naming) + static const CString Externref; // NOLINT (readability-identifier-naming) + static bool Valid(const CString &type) + { + return type == Array || type == Null || type == Node || type == Regexp || type == Map || type == Set || + type == Weakmap || type == Iterator || type == Generator || type == Error || type == Proxy || + type == Promise || type == Typedarray || type == Arraybuffer || type == Dataview || type == I32 || + type == I64 || type == F32 || type == F64 || type == V128 || type == Externref; + } + }; + struct ClassName { + static const CString Object; // NOLINT (readability-identifier-naming) + static const CString Function; // NOLINT (readability-identifier-naming) + static const CString Array; // NOLINT (readability-identifier-naming) + static const CString Regexp; // NOLINT (readability-identifier-naming) + static const CString Date; // NOLINT (readability-identifier-naming) + static const CString Map; // NOLINT (readability-identifier-naming) + static const CString Set; // NOLINT (readability-identifier-naming) + static const CString Weakmap; // NOLINT (readability-identifier-naming) + static const CString Weakset; // NOLINT (readability-identifier-naming) + static const CString ArrayIterator; // NOLINT (readability-identifier-naming) + static const CString StringIterator; // NOLINT (readability-identifier-naming) + static const CString SetIterator; // NOLINT (readability-identifier-naming) + static const CString MapIterator; // NOLINT (readability-identifier-naming) + static const CString Iterator; // NOLINT (readability-identifier-naming) + static const CString Error; // NOLINT (readability-identifier-naming) + static const CString Proxy; // NOLINT (readability-identifier-naming) + static const CString Promise; // NOLINT (readability-identifier-naming) + static const CString Typedarray; // NOLINT (readability-identifier-naming) + static const CString Arraybuffer; // NOLINT (readability-identifier-naming) + static const CString Global; // NOLINT (readability-identifier-naming) + static bool Valid(const CString &type) + { + return type == Object || type == Array || type == Regexp || type == Date || type == Map || type == Set || + type == Weakmap || type == Weakset || type == ArrayIterator || type == StringIterator || + type == Error || type == SetIterator || type == MapIterator || type == Iterator || type == Proxy || + type == Promise || type == Typedarray || type == Arraybuffer || type == Function; + } + }; + static const CString ObjectDescription; // NOLINT (readability-identifier-naming) + static const CString GlobalDescription; // NOLINT (readability-identifier-naming) + static const CString ProxyDescription; // NOLINT (readability-identifier-naming) + static const CString PromiseDescription; // NOLINT (readability-identifier-naming) + static const CString ArrayIteratorDescription; // NOLINT (readability-identifier-naming) + static const CString StringIteratorDescription; // NOLINT (readability-identifier-naming) + static const CString SetIteratorDescription; // NOLINT (readability-identifier-naming) + static const CString MapIteratorDescription; // NOLINT (readability-identifier-naming) + static const CString WeakMapDescription; // NOLINT (readability-identifier-naming) + static const CString WeakSetDescription; // NOLINT (readability-identifier-naming) + +private: + NO_COPY_SEMANTIC(RemoteObject); + NO_MOVE_SEMANTIC(RemoteObject); + + CString type_ {}; + std::optional subtype_ {}; + std::optional className_ {}; + std::optional> value_ {}; + std::optional unserializableValue_ {}; + std::optional description_ {}; + std::optional objectId_ {}; +}; + +class PrimitiveRemoteObject final : public RemoteObject { +public: + explicit PrimitiveRemoteObject(const EcmaVM *ecmaVm, const Local &tagged); + virtual ~PrimitiveRemoteObject() override = default; +}; + +class StringRemoteObject final : public RemoteObject { +public: + explicit StringRemoteObject(const EcmaVM *ecmaVm, const Local &tagged) + { + SetType(RemoteObject::TypeName::String).SetValue(tagged); + } + virtual ~StringRemoteObject() = default; +}; + +class SymbolRemoteObject final : public RemoteObject { +public: + explicit SymbolRemoteObject(const EcmaVM *ecmaVm, const Local &tagged) + { + SetType(RemoteObject::TypeName::Symbol).SetDescription(DescriptionForSymbol(ecmaVm, Local(tagged))); + } + virtual ~SymbolRemoteObject() override = default; + +private: + CString DescriptionForSymbol(const EcmaVM *ecmaVm, const Local &tagged) const; +}; + +class FunctionRemoteObject final : public RemoteObject { +public: + FunctionRemoteObject(const EcmaVM *ecmaVm, const Local &tagged) + { + SetType(RemoteObject::TypeName::Function) + .SetClassName(RemoteObject::ClassName::Function) + .SetDescription(DescriptionForFunction(ecmaVm, tagged)); + } + virtual ~FunctionRemoteObject() override = default; + +private: + CString DescriptionForFunction(const EcmaVM *ecmaVm, const Local &tagged) const; +}; + +class ObjectRemoteObject final : public RemoteObject { +public: + explicit ObjectRemoteObject(const EcmaVM *ecmaVm, const Local &tagged, const CString &classname) + { + SetType(RemoteObject::TypeName::Object) + .SetClassName(classname) + .SetDescription(DescriptionForObject(ecmaVm, tagged)); + } + explicit ObjectRemoteObject( + const EcmaVM *ecmaVm, const Local &tagged, const CString &classname, const CString &subtype) + { + SetType(RemoteObject::TypeName::Object) + .SetSubType(subtype) + .SetClassName(classname) + .SetDescription(DescriptionForObject(ecmaVm, tagged)); + } + virtual ~ObjectRemoteObject() override = default; + static CString DescriptionForObject(const EcmaVM *ecmaVm, const Local &tagged); + +private: + static CString DescriptionForArray(const EcmaVM *ecmaVm, const Local &tagged); + static CString DescriptionForRegexp(const EcmaVM *ecmaVm, const Local &tagged); + static CString DescriptionForDate(const EcmaVM *ecmaVm, const Local &tagged); + static CString DescriptionForMap(const Local &tagged); + static CString DescriptionForSet(const Local &tagged); + static CString DescriptionForError(const EcmaVM *ecmaVm, const Local &tagged); + static CString DescriptionForArrayBuffer(const EcmaVM *ecmaVm, const Local &tagged); +}; + +// Runtime.ExceptionDetails +class ExceptionDetails final : public PtBaseTypes { +public: + ExceptionDetails() = default; + ~ExceptionDetails() override = default; + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + int32_t GetExceptionId() const + { + return exceptionId_; + } + + ExceptionDetails &SetExceptionId(int32_t exceptionId) + { + exceptionId_ = exceptionId; + return *this; + } + + CString GetText() const + { + return text_; + } + + ExceptionDetails &SetText(const CString &text) + { + text_ = text; + return *this; + } + + int32_t GetLine() const + { + return line_; + } + + ExceptionDetails &SetLine(int32_t line) + { + line_ = line; + return *this; + } + + int32_t GetColumn() const + { + return column_; + } + + ExceptionDetails &SetColumn(int32_t column) + { + column_ = column; + return *this; + } + + ScriptId GetScriptId() const + { + return scriptId_.value_or(""); + } + + ExceptionDetails &SetScriptId(const ScriptId &scriptId) + { + scriptId_ = scriptId; + return *this; + } + + bool HasScriptId() const + { + return scriptId_.has_value(); + } + + CString GetUrl() const + { + return url_.value_or(""); + } + + ExceptionDetails &SetUrl(const CString &url) + { + url_ = url; + return *this; + } + + bool HasUrl() const + { + return url_.has_value(); + } + + RemoteObject *GetException() const + { + if (exception_) { + return exception_->get(); + } + return nullptr; + } + + ExceptionDetails &SetException(std::unique_ptr exception) + { + exception_ = std::move(exception); + return *this; + } + + bool HasException() const + { + return exception_.has_value(); + } + + ExecutionContextId GetExecutionContextId() const + { + return executionContextId_.value_or(-1); + } + + ExceptionDetails &SetExecutionContextId(ExecutionContextId executionContextId) + { + executionContextId_ = executionContextId; + return *this; + } + + bool HasExecutionContextId() const + { + return executionContextId_.has_value(); + } + +private: + NO_COPY_SEMANTIC(ExceptionDetails); + NO_MOVE_SEMANTIC(ExceptionDetails); + + int32_t exceptionId_ {0}; + CString text_ {}; + int32_t line_ {0}; + int32_t column_ {0}; + std::optional scriptId_ {}; + std::optional url_ {}; + std::optional> exception_ {}; + std::optional executionContextId_ {0}; +}; + +// Runtime.InternalPropertyDescriptor +class InternalPropertyDescriptor final : public PtBaseTypes { +public: + InternalPropertyDescriptor() = default; + ~InternalPropertyDescriptor() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + CString GetName() const + { + return name_; + } + + InternalPropertyDescriptor &SetName(const CString &name) + { + name_ = name; + return *this; + } + + RemoteObject *GetValue() const + { + if (value_) { + return value_->get(); + } + return nullptr; + } + + InternalPropertyDescriptor &SetValue(std::unique_ptr value) + { + value_ = std::move(value); + return *this; + } + + bool HasValue() const + { + return value_.has_value(); + } + +private: + NO_COPY_SEMANTIC(InternalPropertyDescriptor); + NO_MOVE_SEMANTIC(InternalPropertyDescriptor); + + CString name_ {}; + std::optional> value_ {}; +}; + +// Runtime.PrivatePropertyDescriptor +class PrivatePropertyDescriptor final : public PtBaseTypes { +public: + PrivatePropertyDescriptor() = default; + ~PrivatePropertyDescriptor() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + CString GetName() const + { + return name_; + } + + PrivatePropertyDescriptor &SetName(const CString &name) + { + name_ = name; + return *this; + } + + RemoteObject *GetValue() const + { + if (value_) { + return value_->get(); + } + return nullptr; + } + + PrivatePropertyDescriptor &SetValue(std::unique_ptr value) + { + value_ = std::move(value); + return *this; + } + + bool HasValue() const + { + return value_.has_value(); + } + + RemoteObject *GetGet() const + { + if (get_) { + return get_->get(); + } + return nullptr; + } + + PrivatePropertyDescriptor &SetGet(std::unique_ptr get) + { + get_ = std::move(get); + return *this; + } + + bool HasGet() const + { + return get_.has_value(); + } + + RemoteObject *GetSet() const + { + if (set_) { + return set_->get(); + } + return nullptr; + } + + PrivatePropertyDescriptor &SetSet(std::unique_ptr set) + { + set_ = std::move(set); + return *this; + } + + bool HasSet() const + { + return set_.has_value(); + } + +private: + NO_COPY_SEMANTIC(PrivatePropertyDescriptor); + NO_MOVE_SEMANTIC(PrivatePropertyDescriptor); + + CString name_ {}; + std::optional> value_ {}; + std::optional> get_ {}; + std::optional> set_ {}; +}; + +// Runtime.PropertyDescriptor +class PropertyDescriptor final : public PtBaseTypes { +public: + PropertyDescriptor() = default; + ~PropertyDescriptor() override = default; + + static std::unique_ptr FromProperty( + const EcmaVM *ecmaVm, const Local &name, const PropertyAttribute &property); + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + CString GetName() const + { + return name_; + } + + PropertyDescriptor &SetName(const CString &name) + { + name_ = name; + return *this; + } + + RemoteObject *GetValue() const + { + if (value_) { + return value_->get(); + } + return nullptr; + } + + PropertyDescriptor &SetValue(std::unique_ptr value) + { + value_ = std::move(value); + return *this; + } + + bool HasValue() const + { + return value_.has_value(); + } + + bool GetWritable() const + { + return writable_.value_or(false); + } + + PropertyDescriptor &SetWritable(bool writable) + { + writable_ = writable; + return *this; + } + + bool HasWritable() const + { + return writable_.has_value(); + } + + RemoteObject *GetGet() const + { + if (get_) { + return get_->get(); + } + return nullptr; + } + + PropertyDescriptor &SetGet(std::unique_ptr get) + { + get_ = std::move(get); + return *this; + } + + bool HasGet() const + { + return get_.has_value(); + } + + RemoteObject *GetSet() const + { + if (set_) { + return set_->get(); + } + return nullptr; + } + + PropertyDescriptor &SetSet(std::unique_ptr set) + { + set_ = std::move(set); + return *this; + } + + bool HasSet() const + { + return set_.has_value(); + } + + bool GetConfigurable() const + { + return configurable_; + } + + PropertyDescriptor &SetConfigurable(bool configurable) + { + configurable_ = configurable; + return *this; + } + + bool GetEnumerable() const + { + return enumerable_; + } + + PropertyDescriptor &SetEnumerable(bool enumerable) + { + enumerable_ = enumerable; + return *this; + } + + bool GetWasThrown() const + { + return wasThrown_.value_or(false); + } + + PropertyDescriptor &SetWasThrown(bool wasThrown) + { + wasThrown_ = wasThrown; + return *this; + } + + bool HasWasThrown() const + { + return wasThrown_.has_value(); + } + + bool GetIsOwn() const + { + return isOwn_.value_or(false); + } + + PropertyDescriptor &SetIsOwn(bool isOwn) + { + isOwn_ = isOwn; + return *this; + } + + bool HasIsOwn() const + { + return isOwn_.has_value(); + } + + RemoteObject *GetSymbol() const + { + if (symbol_) { + return symbol_->get(); + } + return nullptr; + } + + PropertyDescriptor &SetSymbol(std::unique_ptr symbol) + { + symbol_ = std::move(symbol); + return *this; + } + + bool HasSymbol() const + { + return symbol_.has_value(); + } + +private: + NO_COPY_SEMANTIC(PropertyDescriptor); + NO_MOVE_SEMANTIC(PropertyDescriptor); + + CString name_ {}; + std::optional> value_ {}; + std::optional writable_ {}; + std::optional> get_ {}; + std::optional> set_ {}; + bool configurable_ {false}; + bool enumerable_ {false}; + std::optional wasThrown_ {}; + std::optional isOwn_ {}; + std::optional> symbol_ {}; +}; + +// ========== Debugger types begin +// Debugger.ScriptLanguage +struct ScriptLanguage { + static bool Valid(const CString &language) + { + return language == JavaScript() || language == WebAssembly(); + } + static CString JavaScript() + { + return "JavaScript"; + } + static CString WebAssembly() + { + return "WebAssembly"; + } +}; + +// Debugger.Location +class Location : public PtBaseTypes { +public: + Location() = default; + virtual ~Location() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + ScriptId GetScriptId() const + { + return scriptId_; + } + + Location &SetScriptId(const ScriptId &scriptId) + { + scriptId_ = scriptId; + return *this; + } + + int32_t GetLine() const + { + return line_; + } + + Location &SetLine(int32_t line) + { + line_ = line; + return *this; + } + + int32_t GetColumn() const + { + return column_.value_or(-1); + } + + Location &SetColumn(int32_t column) + { + column_ = column; + return *this; + } + + bool HasColumn() const + { + return column_.has_value(); + } + +private: + NO_COPY_SEMANTIC(Location); + NO_MOVE_SEMANTIC(Location); + + ScriptId scriptId_ {}; + int32_t line_ {0}; + std::optional column_ {}; +}; + +// Debugger.ScriptPosition +class ScriptPosition : public PtBaseTypes { +public: + ScriptPosition() = default; + virtual ~ScriptPosition() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + int32_t GetLine() const + { + return line_; + } + + ScriptPosition &SetLine(int32_t line) + { + line_ = line; + return *this; + } + + int32_t GetColumn() const + { + return column_; + } + + ScriptPosition &SetColumn(int32_t column) + { + column_ = column; + return *this; + } + +private: + NO_COPY_SEMANTIC(ScriptPosition); + NO_MOVE_SEMANTIC(ScriptPosition); + + int32_t line_ {0}; + int32_t column_ {0}; +}; + +// Debugger.SearchMatch +class SearchMatch : public PtBaseTypes { +public: + SearchMatch() = default; + virtual ~SearchMatch() override = default; + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + +private: + NO_COPY_SEMANTIC(SearchMatch); + NO_MOVE_SEMANTIC(SearchMatch); + + int32_t lineNumber_ {0}; + CString lineContent_ {}; +}; + +// Debugger.LocationRange +class LocationRange : public PtBaseTypes { +public: + LocationRange() = default; + virtual ~LocationRange() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + ScriptId GetScriptId() const + { + return scriptId_; + } + + LocationRange &SetScriptId(const ScriptId &scriptId) + { + scriptId_ = scriptId; + return *this; + } + + ScriptPosition *GetStart() const + { + return start_.get(); + } + + LocationRange &SetStart(std::unique_ptr start) + { + start_ = std::move(start); + return *this; + } + + ScriptPosition *GetEnd() const + { + return end_.get(); + } + + LocationRange &SetEnd(std::unique_ptr end) + { + end_ = std::move(end); + return *this; + } + +private: + NO_COPY_SEMANTIC(LocationRange); + NO_MOVE_SEMANTIC(LocationRange); + + ScriptId scriptId_ {}; + std::unique_ptr start_ {nullptr}; + std::unique_ptr end_ {nullptr}; +}; + +// Debugger.BreakLocation +class BreakLocation final : public PtBaseTypes { +public: + BreakLocation() = default; + ~BreakLocation() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + ScriptId GetScriptId() const + { + return scriptId_; + } + + BreakLocation &SetScriptId(const ScriptId &scriptId) + { + scriptId_ = scriptId; + return *this; + } + + int32_t GetLine() const + { + return line_; + } + + BreakLocation &SetLine(size_t line) + { + line_ = line; + return *this; + } + + int32_t GetColumn() const + { + return column_.value_or(-1); + } + + BreakLocation &SetColumn(int32_t column) + { + column_ = column; + return *this; + } + + bool HasColumn() const + { + return column_.has_value(); + } + + /* + * @see {#BreakType} + */ + CString GetType() const + { + return type_.value_or(""); + } + + BreakLocation &SetType(const CString &type) + { + type_ = type; + return *this; + } + + bool HasType() const + { + return type_.has_value(); + } + + struct Type { + static bool Valid(const CString &type) + { + return type == DebuggerStatement() || type == Call() || type == Return(); + } + static CString DebuggerStatement() + { + return "debuggerStatement"; + } + static CString Call() + { + return "call"; + } + static CString Return() + { + return "return"; + } + }; + +private: + NO_COPY_SEMANTIC(BreakLocation); + NO_MOVE_SEMANTIC(BreakLocation); + + ScriptId scriptId_ {}; + int32_t line_ {0}; + std::optional column_ {}; + std::optional type_ {}; +}; +using BreakType = BreakLocation::Type; + +enum class ScopeType : uint8_t { + GLOBAL, + LOCAL, + WITH, + CLOSURE, + CATCH, + BLOCK, + SCRIPT, + EVAL, + MODULE, + WASM_EXPRESSION_STACK +}; + +// Debugger.Scope +class Scope final : public PtBaseTypes { +public: + Scope() = default; + ~Scope() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + /* + * @see {#Scope::Type} + */ + CString GetType() const + { + return type_; + } + + Scope &SetType(const CString &type) + { + type_ = type; + return *this; + } + + RemoteObject *GetObject() const + { + return object_.get(); + } + + Scope &SetObject(std::unique_ptr params) + { + object_ = std::move(params); + return *this; + } + + CString GetName() const + { + return name_.value_or(""); + } + + Scope &SetName(const CString &name) + { + name_ = name; + return *this; + } + + bool HasName() const + { + return name_.has_value(); + } + + Location *GetStartLocation() const + { + if (startLocation_) { + return startLocation_->get(); + } + return nullptr; + } + + Scope &SetStartLocation(std::unique_ptr location) + { + startLocation_ = std::move(location); + return *this; + } + + bool HasStartLocation() const + { + return startLocation_.has_value(); + } + + Location *GetEndLocation() const + { + if (endLocation_) { + return endLocation_->get(); + } + return nullptr; + } + + Scope &SetEndLocation(std::unique_ptr location) + { + endLocation_ = std::move(location); + return *this; + } + + bool HasEndLocation() const + { + return endLocation_.has_value(); + } + + struct Type { + static bool Valid(const CString &type) + { + return type == Global() || type == Local() || type == With() || type == Closure() || type == Catch() || + type == Block() || type == Script() || type == Eval() || type == Module() || + type == WasmExpressionStack(); + } + static CString Global() + { + return "global"; + } + static CString Local() + { + return "local"; + } + static CString With() + { + return "with"; + } + static CString Closure() + { + return "closure"; + } + static CString Catch() + { + return "catch"; + } + static CString Block() + { + return "block"; + } + static CString Script() + { + return "script"; + } + static CString Eval() + { + return "eval"; + } + static CString Module() + { + return "module"; + } + static CString WasmExpressionStack() + { + return "wasm-expression-stack"; + } + }; + +private: + NO_COPY_SEMANTIC(Scope); + NO_MOVE_SEMANTIC(Scope); + + CString type_ {}; + std::unique_ptr object_ {nullptr}; + std::optional name_ {}; + std::optional> startLocation_ {}; + std::optional> endLocation_ {}; +}; + +// Debugger.CallFrame +class CallFrame final : public PtBaseTypes { +public: + CallFrame() = default; + ~CallFrame() override = default; + + static std::unique_ptr Create(const EcmaVM *ecmaVm, const Local ¶ms); + Local ToObject(const EcmaVM *ecmaVm) override; + + CallFrameId GetCallFrameId() const + { + return callFrameId_; + } + + CallFrame &SetCallFrameId(const CallFrameId &callFrameId) + { + callFrameId_ = callFrameId; + return *this; + } + + CString GetFunctionName() const + { + return functionName_; + } + + CallFrame &SetFunctionName(const CString &functionName) + { + functionName_ = functionName; + return *this; + } + + Location *GetFunctionLocation() const + { + if (functionLocation_) { + return functionLocation_->get(); + } + return nullptr; + } + + CallFrame &SetFunctionLocation(std::unique_ptr location) + { + functionLocation_ = std::move(location); + return *this; + } + + bool HasFunctionLocation() const + { + return functionLocation_.has_value(); + } + + Location *GetLocation() const + { + return location_.get(); + } + + CallFrame &SetLocation(std::unique_ptr location) + { + location_ = std::move(location); + return *this; + } + + CString GetUrl() const + { + return url_; + } + + CallFrame &SetUrl(const CString &url) + { + url_ = url; + return *this; + } + + const CVector> *GetScopeChain() const + { + return &scopeChain_; + } + + CallFrame &SetScopeChain(CVector> scopeChain) + { + scopeChain_ = std::move(scopeChain); + return *this; + } + RemoteObject *GetThis() const + { + return this_.get(); + } + + CallFrame &SetThis(std::unique_ptr thisObj) + { + this_ = std::move(thisObj); + return *this; + } + + RemoteObject *GetReturnValue() const + { + if (returnValue_) { + return returnValue_->get(); + } + return nullptr; + } + + CallFrame &SetReturnValue(std::unique_ptr returnValue) + { + returnValue_ = std::move(returnValue); + return *this; + } + + bool HasReturnValue() const + { + return returnValue_.has_value(); + } + +private: + NO_COPY_SEMANTIC(CallFrame); + NO_MOVE_SEMANTIC(CallFrame); + + CallFrameId callFrameId_ {}; + CString functionName_ {}; + std::optional> functionLocation_ {}; + std::unique_ptr location_ {nullptr}; + CString url_ {}; + CVector> scopeChain_ {}; + std::unique_ptr this_ {nullptr}; + std::optional> returnValue_ {}; +}; +} // namespace panda::tooling::ecmascript +#endif diff --git a/ecmascript/tooling/debugger_service.cpp b/ecmascript/tooling/debugger_service.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c1452ebf72db76a31ae72aebada934032bb773f5 --- /dev/null +++ b/ecmascript/tooling/debugger_service.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/debugger_service.h" + +#include "ecmascript/tooling/protocol_handler.h" + +namespace panda::tooling::ecmascript { +static std::unique_ptr g_handler = nullptr; // NOLINT(fuchsia-statically-constructed-objects) + +void InitializeDebugger(const std::function &onResponse, void *vm) +{ + g_handler = std::make_unique(onResponse, vm); +} + +void UninitializeDebugger() +{ + g_handler.reset(); +} + +void DispatchProtocolMessage(const std::string &message) +{ + if (g_handler != nullptr) { + g_handler->SendCommand(DebuggerApi::ConvertToString(message)); + } +} +} // namespace panda::tooling::ecmascript \ No newline at end of file diff --git a/ecmascript/tooling/debugger_service.h b/ecmascript/tooling/debugger_service.h new file mode 100644 index 0000000000000000000000000000000000000000..e788261ffce51b92ce3dab5d4c54a26eae8df914 --- /dev/null +++ b/ecmascript/tooling/debugger_service.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_DEBUGGER_SERVICE_H +#define PANDA_TOOLING_ECMASCRIPT_DEBUGGER_SERVICE_H + +#include +#include + +#include "ecmascript/common.h" + +namespace panda::tooling::ecmascript { +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* End of #ifdef __cplusplus */ + +PUBLIC_API void InitializeDebugger(const std::function &on_response, void *vm); + +PUBLIC_API void UninitializeDebugger(); + +PUBLIC_API void DispatchProtocolMessage(const std::string &message); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* End of #ifdef __cplusplus */ +} // panda::tooling::ecmascript + +#endif \ No newline at end of file diff --git a/ecmascript/tooling/dispatcher.cpp b/ecmascript/tooling/dispatcher.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a47292c1b689f8729eec76738a807638a15fc32 --- /dev/null +++ b/ecmascript/tooling/dispatcher.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/dispatcher.h" + +#include "libpandabase/utils/logger.h" + +#include "ecmascript/tooling/agent/debugger_impl.h" +#include "ecmascript/tooling/agent/runtime_impl.h" +#include "ecmascript/tooling/front_end.h" + +namespace panda::tooling::ecmascript { +DispatchRequest::DispatchRequest(const EcmaVM *ecmaVm, const CString &message) : ecmaVm_(ecmaVm) +{ + Local msgValue = JSON::Parse(ecmaVm, StringRef::NewFromUtf8(ecmaVm, message.c_str())); + if (msgValue->IsException()) { + DebuggerApi::ClearException(ecmaVm); + LOG(ERROR, DEBUGGER) << "json parse throw exception"; + return; + } + if (!msgValue->IsObject()) { + code_ = RequestCode::JSON_PARSE_ERROR; + LOG(ERROR, DEBUGGER) << "json parse error"; + return; + } + ObjectRef *msgObj = ObjectRef::Cast(*msgValue); + + Local idStr = StringRef::NewFromUtf8(ecmaVm, "id"); + Local idResult = msgObj->Get(ecmaVm, idStr); + if (idResult.IsEmpty()) { + code_ = RequestCode::PARSE_ID_ERROR; + LOG(ERROR, DEBUGGER) << "parse id error"; + return; + } + if (!idResult->IsNumber()) { + code_ = RequestCode::ID_FORMAT_ERROR; + LOG(ERROR, DEBUGGER) << "id format error"; + return; + } + callId_ = static_cast(Local(idResult)->Value()); + + Local methodStr = StringRef::NewFromUtf8(ecmaVm, "method"); + Local methodResult = msgObj->Get(ecmaVm, methodStr); + if (methodResult.IsEmpty()) { + code_ = RequestCode::PARSE_METHOD_ERROR; + LOG(ERROR, DEBUGGER) << "parse method error"; + return; + } + if (!methodResult->IsString()) { + code_ = RequestCode::METHOD_FORMAT_ERROR; + LOG(ERROR, DEBUGGER) << "method format error"; + return; + } + CString wholeMethod = + DebuggerApi::ConvertToString(StringRef::Cast(*methodResult)->ToString()); + CString::size_type length = wholeMethod.length(); + CString::size_type indexPoint; + indexPoint = wholeMethod.find_first_of('.', 0); + if (indexPoint == CString::npos || indexPoint == 0 || indexPoint == length - 1) { + code_ = RequestCode::METHOD_FORMAT_ERROR; + LOG(ERROR, DEBUGGER) << "method format error: " << wholeMethod; + return; + } + domain_ = wholeMethod.substr(0, indexPoint); + method_ = wholeMethod.substr(indexPoint + 1, length); + + LOG(DEBUG, DEBUGGER) << "id: " << callId_; + LOG(DEBUG, DEBUGGER) << "domain: " << domain_; + LOG(DEBUG, DEBUGGER) << "method: " << method_; + + Local paramsStr = StringRef::NewFromUtf8(ecmaVm, "params"); + Local paramsValue = msgObj->Get(ecmaVm, paramsStr); + if (paramsValue.IsEmpty()) { + return; + } + if (!paramsValue->IsObject()) { + code_ = RequestCode::PARAMS_FORMAT_ERROR; + LOG(ERROR, DEBUGGER) << "params format error"; + return; + } + params_ = paramsValue; +} + +DispatchResponse DispatchResponse::Create(ResponseCode code, const CString &msg) +{ + DispatchResponse response; + response.code_ = code; + response.errorMsg_ = msg; + return response; +} + +DispatchResponse DispatchResponse::Create(std::optional error) +{ + DispatchResponse response; + if (error.has_value()) { + response.code_ = ResponseCode::NOK; + response.errorMsg_ = DebuggerApi::ConvertToString(error->GetMessage()); + } + return response; +} + +DispatchResponse DispatchResponse::Ok() +{ + return DispatchResponse(); +} + +DispatchResponse DispatchResponse::Fail(const CString &message) +{ + DispatchResponse response; + response.code_ = ResponseCode::NOK; + response.errorMsg_ = message; + return response; +} + +void DispatcherBase::SendResponse(const DispatchRequest &request, const DispatchResponse &response, + std::unique_ptr result) +{ + if (frontend_ != nullptr) { + frontend_->SendResponse(request, response, std::move(result)); + } +} + +Dispatcher::Dispatcher(FrontEnd *front) +{ + std::unique_ptr backend = std::make_unique(front); + dispatchers_["Runtime"] = + std::make_unique(front, std::make_unique(backend.get())); + dispatchers_["Debugger"] = + std::make_unique(front, std::make_unique(std::move(backend))); +} + +void Dispatcher::Dispatch(const DispatchRequest &request) +{ + if (!request.IsValid()) { + LOG(ERROR, DEBUGGER) << "Unknown request"; + return; + } + CString domain = request.GetDomain(); + auto dispatcher = dispatchers_.find(domain); + if (dispatcher != dispatchers_.end()) { + dispatcher->second->Dispatch(request); + } else { + LOG(ERROR, DEBUGGER) << "unknown domain: " << domain; + } +} +} // namespace panda::tooling::ecmascript diff --git a/ecmascript/tooling/dispatcher.h b/ecmascript/tooling/dispatcher.h new file mode 100644 index 0000000000000000000000000000000000000000..0df3225af5fc5d7fae0b83a5152d80c0b0bf97c5 --- /dev/null +++ b/ecmascript/tooling/dispatcher.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_DISPATCHER_H +#define PANDA_TOOLING_ECMASCRIPT_DISPATCHER_H + +#include +#include + +#include "libpandabase/macros.h" +#include "ecmascript/napi/include/jsnapi.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/mem/c_string.h" +#include "include/tooling/debug_interface.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::CMap; +using panda::ecmascript::CString; +class FrontEnd; +class PtBaseReturns; +class PtBaseEvents; + +enum class RequestCode : uint8_t { + OK = 0, + NOK, + + // Json parse errors + JSON_PARSE_ERROR, + PARSE_ID_ERROR, + ID_FORMAT_ERROR, + PARSE_METHOD_ERROR, + METHOD_FORMAT_ERROR, + PARSE_PARAMS_ERROR, + PARAMS_FORMAT_ERROR +}; + +enum class ResponseCode : uint8_t { OK, NOK }; + +class DispatchRequest { +public: + explicit DispatchRequest(const EcmaVM *ecmaVm, const CString &message); + + bool IsValid() const + { + return code_ == RequestCode::OK; + } + int32_t GetCallId() const + { + return callId_; + } + Local GetParams() const + { + return params_; + } + CString GetDomain() const + { + return domain_; + } + CString GetMethod() const + { + return method_; + } + const EcmaVM *GetEcmaVM() const + { + return ecmaVm_; + } + +private: + const EcmaVM *ecmaVm_ {nullptr}; + int32_t callId_ {-1}; + CString domain_ {}; + CString method_ {}; + Local params_ {}; + RequestCode code_ {RequestCode::OK}; + CString errorMsg_ {}; +}; + +class DispatchResponse { +public: + bool IsOk() const + { + return code_ == ResponseCode::OK; + } + + ResponseCode GetError() const + { + return code_; + } + + CString GetMessage() const + { + return errorMsg_; + } + + static DispatchResponse Create(ResponseCode code, const CString &msg = ""); + static DispatchResponse Create(std::optional error); + static DispatchResponse Ok(); + static DispatchResponse Fail(const CString &message); + +private: + DispatchResponse() = default; + + ResponseCode code_ {ResponseCode::OK}; + CString errorMsg_ {}; +}; + +class DispatcherBase { +public: + explicit DispatcherBase(FrontEnd *frontend) : frontend_(frontend) {} + virtual ~DispatcherBase() + { + frontend_ = nullptr; + }; + virtual void Dispatch(const DispatchRequest &request) = 0; + +protected: + void SendResponse(const DispatchRequest &request, const DispatchResponse &response, + std::unique_ptr result); + +private: + FrontEnd *frontend_{nullptr}; + + NO_COPY_SEMANTIC(DispatcherBase); + NO_MOVE_SEMANTIC(DispatcherBase); +}; + +class Dispatcher { +public: + explicit Dispatcher(FrontEnd *front); + ~Dispatcher() = default; + void Dispatch(const DispatchRequest &request); + +private: + CMap> dispatchers_ {}; + + NO_COPY_SEMANTIC(Dispatcher); + NO_MOVE_SEMANTIC(Dispatcher); +}; +} // namespace panda::tooling::ecmascript +#endif \ No newline at end of file diff --git a/ecmascript/tooling/front_end.h b/ecmascript/tooling/front_end.h new file mode 100644 index 0000000000000000000000000000000000000000..d66622b2eb8f50bdafae1f13a6fa55b988c3c32b --- /dev/null +++ b/ecmascript/tooling/front_end.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_FRONT_END_H +#define PANDA_TOOLING_ECMASCRIPT_FRONT_END_H + +#include "libpandabase/macros.h" +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "ecmascript/tooling/dispatcher.h" + +namespace panda::tooling::ecmascript { +class FrontEnd { +public: + FrontEnd() = default; + virtual ~FrontEnd() = default; + + virtual void WaitForDebugger(const EcmaVM *ecmaVm) = 0; + virtual void RunIfWaitingForDebugger() = 0; + virtual void ProcessCommand(const EcmaVM *ecmaVm) = 0; + virtual void SendResponse(const DispatchRequest &request, const DispatchResponse &response, + std::unique_ptr result) = 0; + virtual void SendNotification(const EcmaVM *ecmaVm, std::unique_ptr events) = 0; + +private: + NO_COPY_SEMANTIC(FrontEnd); + NO_MOVE_SEMANTIC(FrontEnd); +}; +} // namespace panda::tooling::ecmascript + +#endif \ No newline at end of file diff --git a/ecmascript/tooling/interface/debugger_api.cpp b/ecmascript/tooling/interface/debugger_api.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eeaf915bc04ab17a9ecde229c29fd840541f7e1f --- /dev/null +++ b/ecmascript/tooling/interface/debugger_api.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/interface/debugger_api.h" + +#include "ecmascript/base/number_helper.h" +#include "ecmascript/class_linker/program_object-inl.h" +#include "ecmascript/interpreter/frame_handler.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_method.h" +#include "ecmascript/mem/c_string.h" +#include "ecmascript/napi/jsnapi_helper-inl.h" +#include "ecmascript/tooling/interface/js_debugger.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::JSHandle; +using panda::ecmascript::JSTaggedValue; +using panda::ecmascript::Program; +using panda::ecmascript::base::ALLOW_BINARY; +using panda::ecmascript::base::ALLOW_HEX; +using panda::ecmascript::base::ALLOW_OCTAL; +using panda::ecmascript::base::NumberHelper; + +// CString +uint64_t DebuggerApi::CStringToULL(const CString &str) +{ + return panda::ecmascript::CStringToULL(str); +} + +CString DebuggerApi::ToCString(int32_t number) +{ + return panda::ecmascript::ToCString(number); +} + +CString DebuggerApi::ConvertToString(const std::string &str) +{ + return panda::ecmascript::ConvertToString(str); +} + +// EcmaFrameHandler +uint32_t DebuggerApi::GetStackDepth(const EcmaVM *ecmaVm) +{ + uint32_t count = 0; + EcmaFrameHandler frameHandler(ecmaVm->GetJSThread()); + for (; frameHandler.HasFrame(); frameHandler.PrevFrame()) { + ++count; + } + return count; +} + +bool DebuggerApi::StackWalker(const EcmaVM *ecmaVm, std::function func) +{ + EcmaFrameHandler frameHandler(ecmaVm->GetJSThread()); + for (; frameHandler.HasFrame(); frameHandler.PrevFrame()) { + StackState state = func(&frameHandler); + if (state == StackState::CONTINUE) { + continue; + } + if (state == StackState::FAILED) { + return false; + } + return true; + } + return true; +} + +uint32_t DebuggerApi::GetBytecodeOffset(const EcmaVM *ecmaVm) +{ + return EcmaFrameHandler(ecmaVm->GetJSThread()).GetBytecodeOffset(); +} + +JSMethod *DebuggerApi::GetMethod(const EcmaVM *ecmaVm) +{ + return EcmaFrameHandler(ecmaVm->GetJSThread()).GetMethod(); +} + +Local DebuggerApi::GetVRegValue(const EcmaVM *ecmaVm, size_t index) +{ + auto value = EcmaFrameHandler(ecmaVm->GetJSThread()).GetVRegValue(index); + JSHandle handledValue(ecmaVm->GetJSThread(), value); + return JSNApiHelper::ToLocal(handledValue); +} + +void DebuggerApi::SetVRegValue(const EcmaVM *ecmaVm, size_t index, Local value) +{ + return EcmaFrameHandler(ecmaVm->GetJSThread()).SetVRegValue(index, JSNApiHelper::ToJSTaggedValue(*value)); +} + +uint32_t DebuggerApi::GetBytecodeOffset(const EcmaFrameHandler *frameHandler) +{ + return frameHandler->GetBytecodeOffset(); +} + +JSMethod *DebuggerApi::GetMethod(const EcmaFrameHandler *frameHandler) +{ + return frameHandler->GetMethod(); +} + +Local DebuggerApi::GetVRegValue(const EcmaVM *ecmaVm, const EcmaFrameHandler *frameHandler, size_t index) +{ + auto value = frameHandler->GetVRegValue(index); + JSHandle handledValue(ecmaVm->GetJSThread(), value); + return JSNApiHelper::ToLocal(handledValue); +} + +// JSThread +Local DebuggerApi::GetException(const EcmaVM *ecmaVm) +{ + auto exception = ecmaVm->GetJSThread()->GetException(); + JSHandle handledException(ecmaVm->GetJSThread(), exception); + return JSNApiHelper::ToLocal(handledException); +} + +void DebuggerApi::SetException(const EcmaVM *ecmaVm, Local exception) +{ + ecmaVm->GetJSThread()->SetException(JSNApiHelper::ToJSTaggedValue(*exception)); +} + +void DebuggerApi::ClearException(const EcmaVM *ecmaVm) +{ + return ecmaVm->GetJSThread()->ClearException(); +} + +// EcmaVM +const panda_file::File *DebuggerApi::FindPandaFile(const EcmaVM *ecmaVm, const CString &fileName) +{ + const panda_file::File *pfs = nullptr; + ecmaVm->EnumeratePandaFiles([&pfs, fileName]([[maybe_unused]] const Program *pg, const panda_file::File *pf) { + if (ConvertToString(pf->GetFilename()) == fileName) { + pfs = pf; + return false; + } + return true; + }); + + return pfs; +} + +// NumberHelper +double DebuggerApi::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix) +{ + return NumberHelper::StringToDouble(start, end, radix, ALLOW_BINARY | ALLOW_HEX | ALLOW_OCTAL); +} + +// JSDebugger +JSDebugger *DebuggerApi::CreateJSDebugger(Runtime *runtime, const EcmaVM *ecmaVm) +{ + return new JSDebugger(runtime, ecmaVm); +} + +void DebuggerApi::DestroyJSDebugger(JSDebugger *debugger) +{ + delete debugger; +} + +std::optional DebuggerApi::RegisterHooks(JSDebugger *debugger, PtHooks *hooks) +{ + return debugger->RegisterHooks(hooks); +} + +std::optional DebuggerApi::SetBreakpoint(JSDebugger *debugger, const PtLocation &location) +{ + return debugger->SetBreakpoint(location); +} + +std::optional DebuggerApi::RemoveBreakpoint(JSDebugger *debugger, const PtLocation &location) +{ + return debugger->RemoveBreakpoint(location); +} +} // namespace panda::tooling::ecmascript diff --git a/ecmascript/tooling/interface/debugger_api.h b/ecmascript/tooling/interface/debugger_api.h new file mode 100644 index 0000000000000000000000000000000000000000..78a976b8f0ee75f05f80ef669fac47918d2a41b0 --- /dev/null +++ b/ecmascript/tooling/interface/debugger_api.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_DEBUGGER_API_H +#define PANDA_TOOLING_ECMASCRIPT_DEBUGGER_API_H + +#include + +#include "ecmascript/common.h" +#include "ecmascript/mem/c_string.h" +#include "ecmascript/napi/include/jsnapi.h" + +#include "mem/rendezvous.h" +#include "include/runtime_notification.h" +#include "include/tooling/debug_interface.h" + +namespace panda { +namespace tooling::ecmascript { +class JSDebugger; +} // tooling::ecmascript + +namespace ecmascript { +class EcmaFrameHandler; +class EcmaVM; +class JSMethod; +class JSThread; +} // ecmascript +} // panda + +namespace panda::tooling::ecmascript { +using panda::ecmascript::CString; +using panda::ecmascript::EcmaFrameHandler; +using panda::ecmascript::EcmaVM; +using panda::ecmascript::JSMethod; +using panda::ecmascript::JSThread; + +enum StackState { + CONTINUE, + FAILED, + SUCCESS, +}; + +class PUBLIC_API DebuggerApi { +public: + // CString + static uint64_t CStringToULL(const CString &str); + static CString ToCString(int32_t number); + static CString ConvertToString(const std::string &str); + + // EcmaFrameHandler + static uint32_t GetStackDepth(const EcmaVM *ecmaVm); + static bool StackWalker(const EcmaVM *ecmaVm, std::function func); + static uint32_t GetBytecodeOffset(const EcmaVM *ecmaVm); + static JSMethod *GetMethod(const EcmaVM *ecmaVm); + static Local GetVRegValue(const EcmaVM *ecmaVm, size_t index); + static void SetVRegValue(const EcmaVM *ecmaVm, size_t index, Local value); + static uint32_t GetBytecodeOffset(const EcmaFrameHandler *frameHandler); + static JSMethod *GetMethod(const EcmaFrameHandler *frameHandler); + static Local GetVRegValue(const EcmaVM *ecmaVm, const EcmaFrameHandler *frameHandler, size_t index); + + // JSThread + static Local GetException(const EcmaVM *ecmaVm); + static void SetException(const EcmaVM *ecmaVm, Local exception); + static void ClearException(const EcmaVM *ecmaVm); + + // EcmaVM + static const panda_file::File *FindPandaFile(const EcmaVM *ecmaVm, const CString &fileName); + + // NumberHelper + static double StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix); + + // JSDebugger + static JSDebugger *CreateJSDebugger(Runtime *runtime, const EcmaVM *ecmaVm); + static void DestroyJSDebugger(JSDebugger *debugger); + static std::optional RegisterHooks(JSDebugger *debugger, PtHooks *hooks); + static std::optional SetBreakpoint(JSDebugger *debugger, const PtLocation &location); + static std::optional RemoveBreakpoint(JSDebugger *debugger, const PtLocation &location); +}; +} // namespace panda::tooling::ecmascript + +#endif // PANDA_TOOLING_ECMASCRIPT_DEBUGGER_API_H \ No newline at end of file diff --git a/ecmascript/tooling/interface/js_debugger.cpp b/ecmascript/tooling/interface/js_debugger.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e3a7b1df74822a5dc2c86fabc229d0d8257ab94b --- /dev/null +++ b/ecmascript/tooling/interface/js_debugger.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/interface/js_debugger.h" + +#include "ecmascript/class_linker/program_object-inl.h" +#include "ecmascript/js_thread.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::Program; + +std::optional JSDebugger::SetBreakpoint(const PtLocation &location) +{ + JSMethod *method = FindMethod(location); + if (method == nullptr) { + return Error(Error::Type::METHOD_NOT_FOUND, + std::string("Cannot find JSMethod with id ") + std::to_string(location.GetMethodId().GetOffset()) + + " in panda file '" + std::string(location.GetPandaFile()) + "'"); + } + + if (location.GetBytecodeOffset() >= method->GetCodeSize()) { + return Error(Error::Type::INVALID_BREAKPOINT, std::string("Invalid breakpoint location: bytecode offset (") + + std::to_string(location.GetBytecodeOffset()) + + ") >= JSMethod code size (" + + std::to_string(method->GetCodeSize()) + ")"); + } + + if (!breakpoints_.emplace(method, location.GetBytecodeOffset()).second) { + return Error(Error::Type::BREAKPOINT_ALREADY_EXISTS, + std::string("Breakpoint already exists: bytecode offset ") + + std::to_string(location.GetBytecodeOffset())); + } + + return {}; +} + +std::optional JSDebugger::RemoveBreakpoint(const PtLocation &location) +{ + JSMethod *method = FindMethod(location); + if (method == nullptr) { + return Error(Error::Type::METHOD_NOT_FOUND, + std::string("Cannot find JSMethod with id ") + std::to_string(location.GetMethodId().GetOffset()) + + " in panda file '" + std::string(location.GetPandaFile()) + "'"); + } + + if (!RemoveBreakpoint(method, location.GetBytecodeOffset())) { + return Error(Error::Type::BREAKPOINT_NOT_FOUND, "Breakpoint not found"); + } + + return {}; +} + +void JSDebugger::BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset) +{ + ASSERT(bcOffset < method->GetCodeSize() && "code size of current JSMethod less then bcOffset"); + + HandleExceptionThrowEvent(JSThread::Cast(thread), JSMethod::Cast(method), bcOffset); + + // Step event is reported before breakpoint, according to the spec. + HandleStep(JSThread::Cast(thread), JSMethod::Cast(method), bcOffset); + HandleBreakpoint(JSThread::Cast(thread), JSMethod::Cast(method), bcOffset); +} + +bool JSDebugger::HandleBreakpoint([[maybe_unused]] const JSThread *thread, const JSMethod *method, + uint32_t bcOffset) +{ + if (!FindBreakpoint(method, bcOffset)) { + return false; + } + + auto *pf = method->GetPandaFile(); + PtLocation location {pf->GetFilename().c_str(), method->GetFileId(), bcOffset}; + hooks_.Breakpoint(PtThread(ManagedThread::NON_INITIALIZED_THREAD_ID), location); + + return true; +} + +void JSDebugger::HandleExceptionThrowEvent(const JSThread *thread, const JSMethod *method, uint32_t bcOffset) +{ + if (!thread->HasPendingException()) { + return; + } + + auto *pf = method->GetPandaFile(); + PtLocation throwLocation {pf->GetFilename().c_str(), method->GetFileId(), bcOffset}; + + hooks_.Exception(PtThread(ManagedThread::NON_INITIALIZED_THREAD_ID), throwLocation, PtObject(), throwLocation); +} + +bool JSDebugger::HandleStep(const JSThread *thread, const JSMethod *method, uint32_t bcOffset) +{ + auto *pf = method->GetPandaFile(); + PtLocation location {pf->GetFilename().c_str(), method->GetFileId(), bcOffset}; + hooks_.SingleStep(PtThread(thread->GetId()), location); + return true; +} + +bool JSDebugger::FindBreakpoint(const JSMethod *method, uint32_t bcOffset) const +{ + for (const auto &bp : breakpoints_) { + if (bp.GetBytecodeOffset() == bcOffset && bp.GetMethod()->GetPandaFile() == method->GetPandaFile() && + bp.GetMethod()->GetFileId() == method->GetFileId()) { + return true; + } + } + + return false; +} + +bool JSDebugger::RemoveBreakpoint(const JSMethod *method, uint32_t bcOffset) +{ + for (auto it = breakpoints_.begin(); it != breakpoints_.end(); ++it) { + const auto &bp = *it; + if (bp.GetBytecodeOffset() == bcOffset && bp.GetMethod() == method) { + it = breakpoints_.erase(it); + return true; + } + } + + return false; +} + +JSMethod *JSDebugger::FindMethod(const PtLocation &location) const +{ + JSMethod *method = nullptr; + ecmaVm_->EnumeratePandaFiles([&method, location](const Program *pg, const panda_file::File *pf) { + if (std::string(location.GetPandaFile()) == pf->GetFilename()) { + JSMethod *methodsData = pg->GetMethodsData(); + uint32_t numberMethods = pg->GetNumberMethods(); + for (uint32_t i = 0; i < numberMethods; ++i) { + if (methodsData[i].GetFileId() == location.GetMethodId()) { + method = methodsData + i; + return false; + } + } + } + return true; + }); + return method; +} +} // panda::tooling::ecmascript \ No newline at end of file diff --git a/ecmascript/tooling/interface/js_debugger.h b/ecmascript/tooling/interface/js_debugger.h new file mode 100644 index 0000000000000000000000000000000000000000..68c4186c91a3b61aa2f3526a8e163bd39848ec85 --- /dev/null +++ b/ecmascript/tooling/interface/js_debugger.h @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_JS_DEBUGGER_H +#define PANDA_TOOLING_ECMASCRIPT_JS_DEBUGGER_H + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_method.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/tooling/interface/debugger_api.h" +#include "tooling/debugger.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::CUnorderedSet; + +class JSDebugger : public DebugInterface, RuntimeListener { +public: + JSDebugger(const Runtime *runtime, const EcmaVM *vm) : runtime_(runtime), ecmaVm_(vm) + { + auto notificationMgr = runtime_->GetNotificationManager(); + // set EcmaVM rendezvous + notificationMgr->SetRendezvous(ecmaVm_->GetRendezvous()); + notificationMgr->AddListener(this, JSDEBUG_EVENT_MASK); + // set PandaVM rendezvous + notificationMgr->SetRendezvous(runtime_->GetPandaVM()->GetRendezvous()); + } + ~JSDebugger() override + { + auto notificationMgr = runtime_->GetNotificationManager(); + // set EcmaVM rendezvous + notificationMgr->SetRendezvous(ecmaVm_->GetRendezvous()); + notificationMgr->RemoveListener(this, JSDEBUG_EVENT_MASK); + // set PandaVM rendezvous + notificationMgr->SetRendezvous(runtime_->GetPandaVM()->GetRendezvous()); + } + + std::optional RegisterHooks(PtHooks *hooks) override + { + hooks_.SetHooks(hooks); + return {}; + } + std::optional UnregisterHooks() override + { + hooks_.SetHooks(nullptr); + return {}; + } + + std::optional SetBreakpoint(const PtLocation &location) override; + std::optional RemoveBreakpoint(const PtLocation &location) override; + void BytecodePcChanged(ManagedThread *thread, Method *method, uint32_t bcOffset) override; + void LoadModule(std::string_view filename) override + { + hooks_.LoadModule(filename); + } + void VmStart() override + { + hooks_.VmStart(); + } + void VmInitialization(ManagedThread::ThreadId threadId) override + { + hooks_.VmInitialization(PtThread(threadId)); + } + void VmDeath() override + { + hooks_.VmDeath(); + } + + PtLangExt *GetLangExtension() const override + { + return nullptr; + } + Expected GetPtMethod(const PtLocation &location) const override + { + return Unexpected(Error(Error::Type::INVALID_VALUE, "Unsupported GetPtMethod")); + } + std::optional EnableAllGlobalHook() override + { + return {}; + } + std::optional DisableAllGlobalHook() override + { + return {}; + } + std::optional SetNotification(PtThread thread, bool enable, PtHookType hookType) override + { + return {}; + } + Expected, Error> GetCurrentFrame(PtThread thread) const override + { + return Unexpected(Error(Error::Type::INVALID_VALUE, "Unsupported GetCurrentFrame")); + } + std::optional EnumerateFrames(PtThread thread, std::function callback) const override + { + return {}; + } + void ThreadStart(ManagedThread::ThreadId threadId) override {} + void ThreadEnd(ManagedThread::ThreadId threadId) override {} + void GarbageCollectorStart() override {} + void GarbageCollectorFinish() override {} + void ObjectAlloc(BaseClass *klass, ObjectHeader *object, ManagedThread *thread, size_t size) override {} + void ExceptionCatch(const ManagedThread *thread, const Method *method, uint32_t bcOffset) override {} + void MethodEntry(ManagedThread *thread, Method *method) override {} + void MethodExit(ManagedThread *thread, Method *method) override {} + void ClassLoad(Class *klass) override {} + void ClassPrepare(Class *klass) override {} + void MonitorWait(ObjectHeader *object, int64_t timeout) override {} + void MonitorWaited(ObjectHeader *object, bool timedOut) override {} + void MonitorContendedEnter(ObjectHeader *object) override {} + void MonitorContendedEntered(ObjectHeader *object) override {} + + std::optional GetThreadList(PandaVector *threadList) const override + { + return {}; + } + std::optional GetThreadInfo(PtThread thread, ThreadInfo *infoPtr) const override + { + return {}; + } + std::optional SuspendThread(PtThread thread) const override + { + return {}; + } + std::optional ResumeThread(PtThread thread) const override + { + return {}; + } + std::optional SetVariable( + PtThread thread, uint32_t frameDepth, int32_t regNumber, const PtValue &value) const override + { + return {}; + } + std::optional GetVariable( + PtThread thread, uint32_t frameDepth, int32_t regNumber, PtValue *result) const override + { + return {}; + } + std::optional GetProperty( + [[maybe_unused]] PtObject object, [[maybe_unused]] PtProperty property, PtValue *value) const override + { + return {}; + } + std::optional SetProperty([[maybe_unused]] PtObject object, [[maybe_unused]] PtProperty property, + [[maybe_unused]] const PtValue &value) const override + { + return {}; + } + std::optional EvaluateExpression([[maybe_unused]] PtThread thread, [[maybe_unused]] uint32_t frameNumber, + ExpressionWrapper expr, PtValue *result) const override + { + return {}; + } + std::optional RetransformClasses( + [[maybe_unused]] int32_t classCount, [[maybe_unused]] const PtClass *classes) const override + { + return {}; + } + std::optional RedefineClasses( + [[maybe_unused]] int32_t classCount, [[maybe_unused]] const PandaClassDefinition *classes) const override + { + return {}; + } + std::optional RestartFrame( + [[maybe_unused]] PtThread thread, [[maybe_unused]] uint32_t frameNumber) const override + { + return {}; + } + std::optional SetAsyncCallStackDepth([[maybe_unused]] uint32_t maxDepth) const override + { + return {}; + } + std::optional AwaitPromise([[maybe_unused]] PtObject promiseObject, PtValue *result) const override + { + return {}; + } + std::optional CallFunctionOn([[maybe_unused]] PtObject object, [[maybe_unused]] PtMethod method, + [[maybe_unused]] const PandaVector &arguments, PtValue *returnValue) const override + { + return {}; + } + std::optional GetProperties(uint32_t *countPtr, [[maybe_unused]] char ***propertyPtr) const override + { + return {}; + } + std::optional NotifyFramePop(PtThread thread, uint32_t depth) const override + { + return {}; + } + std::optional SetPropertyAccessWatch(PtClass klass, PtProperty property) override + { + return {}; + } + std::optional ClearPropertyAccessWatch(PtClass klass, PtProperty property) override + { + return {}; + } + std::optional SetPropertyModificationWatch(PtClass klass, PtProperty property) override + { + return {}; + } + std::optional ClearPropertyModificationWatch(PtClass klass, PtProperty property) override + { + return {}; + } + +private: + static constexpr uint32_t JSDEBUG_EVENT_MASK = RuntimeNotificationManager::Event::LOAD_MODULE | + RuntimeNotificationManager::Event::BYTECODE_PC_CHANGED | + RuntimeNotificationManager::Event::VM_EVENTS; + + JSMethod *FindMethod(const PtLocation &location) const; + bool FindBreakpoint(const JSMethod *method, uint32_t bcOffset) const; + bool RemoveBreakpoint(const JSMethod *method, uint32_t bcOffset); + bool HandleBreakpoint(const JSThread *thread, const JSMethod *method, uint32_t bcOffset); + void HandleExceptionThrowEvent(const JSThread *thread, const JSMethod *method, uint32_t bcOffset); + bool HandleStep(const JSThread *thread, const JSMethod *method, uint32_t bcOffset); + + const Runtime *runtime_; + const EcmaVM *ecmaVm_; + PtHooksWrapper hooks_; + + CUnorderedSet breakpoints_ {}; +}; +} // namespace panda::tooling::ecmascript + +#endif // PANDA_TOOLING_ECMASCRIPT_JS_DEBUGGER_H diff --git a/ecmascript/tooling/protocol_handler.cpp b/ecmascript/tooling/protocol_handler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b5546c067afb21ab62bc5b968edcf3ee4095830 --- /dev/null +++ b/ecmascript/tooling/protocol_handler.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/string_helper.h" + +#include "ecmascript/tooling/agent/debugger_impl.h" +#include "ecmascript/tooling/protocol_handler.h" +#include "utils/logger.h" + +namespace panda::tooling::ecmascript { +ProtocolHandler::ProtocolHandler(std::function callback, const void *vm) + : callback_(std::move(callback)), vm_(static_cast(vm)) +{ + dispatcher_ = std::make_unique(this); +} + +void ProtocolHandler::WaitForDebugger(const EcmaVM *ecmaVm) +{ + waitingForDebugger_ = true; + ProcessCommand(ecmaVm); +} + +void ProtocolHandler::RunIfWaitingForDebugger() +{ + waitingForDebugger_ = false; +} + +void ProtocolHandler::ProcessCommand(const EcmaVM *ecmaVm) +{ + CVector msgs; + do { + queueLock_.Lock(); + if (waitingForDebugger_ && msgQueue_.empty()) { + queueCond_.Wait(&queueLock_); + } + while (!msgQueue_.empty()) { + msgs.emplace_back(std::move(msgQueue_.front())); + msgQueue_.pop(); + } + queueLock_.Unlock(); + + for (const auto &msg : msgs) { + LOG(DEBUG, DEBUGGER) << "ProtocolHandler::ProcessCommand: " << msg; + Local exception = DebuggerApi::GetException(ecmaVm); + if (!exception->IsHole()) { + DebuggerApi::ClearException(ecmaVm); + } + dispatcher_->Dispatch(DispatchRequest(ecmaVm, msg)); + DebuggerApi::ClearException(ecmaVm); + if (!exception->IsHole()) { + DebuggerApi::SetException(ecmaVm, exception); + } + } + msgs.clear(); + } while (waitingForDebugger_); +} + +void ProtocolHandler::SendCommand(const CString &msg) +{ + queueLock_.Lock(); + msgQueue_.push(msg); + queueCond_.Signal(); + queueLock_.Unlock(); +} + +void ProtocolHandler::SendResponse( + const DispatchRequest &request, const DispatchResponse &response, std::unique_ptr result) +{ + LOG(INFO, DEBUGGER) << "ProtocolHandler::SendResponse: " + << (response.IsOk() ? "success" : "failed: " + response.GetMessage()); + const EcmaVM *ecmaVm = request.GetEcmaVM(); + + Local reply = PtBaseTypes::NewObject(ecmaVm); + reply->Set( + ecmaVm, StringRef::NewFromUtf8(ecmaVm, "id"), IntegerRef::New(ecmaVm, request.GetCallId())); + Local resultObj; + if (response.IsOk() && result != nullptr) { + resultObj = result->ToObject(ecmaVm); + } else { + resultObj = CreateErrorReply(ecmaVm, response); + } + reply->Set(ecmaVm, StringRef::NewFromUtf8(ecmaVm, "result"), Local(resultObj)); + SendReply(ecmaVm, reply); +} + +void ProtocolHandler::SendNotification(const EcmaVM *ecmaVm, std::unique_ptr events) +{ + if (events == nullptr) { + return; + } + LOG(DEBUG, DEBUGGER) << "ProtocolHandler::SendNotification: " << events->GetName(); + SendReply(ecmaVm, events->ToObject(ecmaVm)); +} + +void ProtocolHandler::SendReply(const EcmaVM *ecmaVm, Local reply) +{ + Local str = JSON::Stringify(ecmaVm, reply); + if (str->IsException()) { + DebuggerApi::ClearException(ecmaVm); + LOG(ERROR, DEBUGGER) << "json stringifier throw exception"; + return; + } + if (!str->IsString()) { + LOG(ERROR, DEBUGGER) << "ProtocolHandler::SendReply: json stringify error"; + return; + } + + callback_(StringRef::Cast(*str)->ToString()); +} + +Local ProtocolHandler::CreateErrorReply(const EcmaVM *ecmaVm, const DispatchResponse &response) +{ + Local result = PtBaseTypes::NewObject(ecmaVm); + + if (!response.IsOk()) { + result->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "code")), + IntegerRef::New(ecmaVm, static_cast(response.GetError()))); + result->Set(ecmaVm, + Local(StringRef::NewFromUtf8(ecmaVm, "message")), + Local(StringRef::NewFromUtf8(ecmaVm, response.GetMessage().c_str()))); + } + + return result; +} +} // namespace panda::tooling::ecmascript diff --git a/ecmascript/tooling/protocol_handler.h b/ecmascript/tooling/protocol_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..39c76e38ab5977b5c45e2b3ef151168b85f80e68 --- /dev/null +++ b/ecmascript/tooling/protocol_handler.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_ECMASCRIPT_PROTOCOL_HANDLER_H +#define PANDA_TOOLING_ECMASCRIPT_PROTOCOL_HANDLER_H + +#include +#include +#include + +#include "ecmascript/tooling/front_end.h" + +namespace panda::tooling::ecmascript { +class ProtocolHandler final : public FrontEnd { +public: + explicit ProtocolHandler(std::function callback, const void *vm); + ~ProtocolHandler() override = default; + + void WaitForDebugger(const EcmaVM *ecmaVm) override; + void RunIfWaitingForDebugger() override; + void ProcessCommand(const EcmaVM *ecmaVm) override; + void SendCommand(const CString &msg); + void SendResponse(const DispatchRequest &request, const DispatchResponse &response, + std::unique_ptr result) override; + void SendNotification(const EcmaVM *ecmaVm, std::unique_ptr events) override; + const EcmaVM *GetEcmaVM() + { + return vm_; + } + +private: + NO_MOVE_SEMANTIC(ProtocolHandler); + NO_COPY_SEMANTIC(ProtocolHandler); + Local CreateErrorReply(const EcmaVM *ecmaVm, const DispatchResponse &response); + void SendReply(const EcmaVM *ecmaVm, Local reply); + + std::function callback_; + std::unique_ptr dispatcher_ {}; + + bool waitingForDebugger_ {false}; + CQueue msgQueue_ {}; + os::memory::Mutex queueLock_; + os::memory::ConditionVariable queueCond_ GUARDED_BY(queueLock_); + const EcmaVM *vm_ {nullptr}; +}; +} // namespace panda::tooling::ecmascript + +#endif \ No newline at end of file diff --git a/ecmascript/tooling/pt_js_extractor.cpp b/ecmascript/tooling/pt_js_extractor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c84f898e04b1c86c76b9e5b701e83076e36c794e --- /dev/null +++ b/ecmascript/tooling/pt_js_extractor.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/interface/debugger_api.h" +#include "ecmascript/tooling/pt_js_extractor.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::EcmaFrameHandler; +using panda::ecmascript::JSTaggedType; +uint32_t PtJSExtractor::SingleStepper::GetStackDepth() const +{ + return DebuggerApi::GetStackDepth(ecmaVm_); +} + +bool PtJSExtractor::SingleStepper::InStepRange(uint32_t pc) const +{ + for (const auto &range : stepRanges_) { + if (pc >= range.start_bc_offset && pc < range.end_bc_offset) { + return true; + } + } + return false; +} + +bool PtJSExtractor::SingleStepper::StepComplete(uint32_t bcOffset) const +{ + JSMethod *method = DebuggerApi::GetMethod(ecmaVm_); + uint32_t stackDepth = GetStackDepth(); + + switch (type_) { + case Type::INTO: { + if (method_ == method && InStepRange(bcOffset)) { + return false; + } + break; + } + case Type::OVER: { + if (stackDepth_ < stackDepth) { + return false; + } + if (stackDepth_ == stackDepth && InStepRange(bcOffset)) { + return false; + } + break; + } + case SingleStepper::Type::OUT: { + if (stackDepth_ <= stackDepth) { + return false; + } + break; + } + default: { + return false; + } + } + + return true; +} + +std::unique_ptr PtJSExtractor::GetStepIntoStepper(const EcmaVM *ecmaVm) +{ + return GetStepper(ecmaVm, SingleStepper::Type::INTO); +} + +std::unique_ptr PtJSExtractor::GetStepOverStepper(const EcmaVM *ecmaVm) +{ + return GetStepper(ecmaVm, SingleStepper::Type::OVER); +} + +std::unique_ptr PtJSExtractor::GetStepOutStepper(const EcmaVM *ecmaVm) +{ + return GetStepper(ecmaVm, SingleStepper::Type::OUT); +} + +CList PtJSExtractor::GetStepRanges(File::EntityId methodId, uint32_t offset) +{ + CList ranges; + auto table = GetLineNumberTable(methodId); + MatchWithOffset( + [table, &ranges](int32_t line) -> bool { + for (auto it = table.begin(); it != table.end();) { + auto next = it + 1; + if (static_cast(it->line) == line) { + ranges.push_back({it->offset, next != table.end() ? next->offset : UINT32_MAX}); + } + it = next; + } + return true; + }, + methodId, offset); + + return ranges; +} + +std::unique_ptr PtJSExtractor::GetStepper(const EcmaVM *ecmaVm, SingleStepper::Type type) +{ + JSMethod *method = DebuggerApi::GetMethod(ecmaVm); + ASSERT(method != nullptr); + + if (type == SingleStepper::Type::OUT) { + return std::make_unique(ecmaVm, method, CList {}, type); + } + + CList ranges = GetStepRanges(method->GetFileId(), DebuggerApi::GetBytecodeOffset(ecmaVm)); + return std::make_unique(ecmaVm, method, std::move(ranges), type); +} +} // namespace panda::tooling::ecmascript diff --git a/ecmascript/tooling/pt_js_extractor.h b/ecmascript/tooling/pt_js_extractor.h new file mode 100644 index 0000000000000000000000000000000000000000..581a71ec5331e822c10e5c07fd5d7d646df071b5 --- /dev/null +++ b/ecmascript/tooling/pt_js_extractor.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_JS_EXTRACTOR_H +#define PANDA_TOOLING_JS_EXTRACTOR_H + +#include "ecmascript/js_method.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/c_containers.h" +#include "libpandafile/debug_info_extractor.h" +#include "libpandabase/macros.h" + +namespace panda::tooling::ecmascript { +using panda::ecmascript::CList; +using panda::ecmascript::EcmaVM; +using panda::ecmascript::JSMethod; +using panda::panda_file::DebugInfoExtractor; +using panda::panda_file::File; + +class PtJSExtractor : public DebugInfoExtractor { +public: + class SingleStepper { + public: + enum class Type { INTO, OVER, OUT }; + SingleStepper(const EcmaVM *ecmaVm, JSMethod *method, CList stepRanges, Type type) + : ecmaVm_(ecmaVm), + method_(method), + stepRanges_(std::move(stepRanges)), + stackDepth_(GetStackDepth()), + type_(type) + { + } + virtual ~SingleStepper() = default; + NO_COPY_SEMANTIC(SingleStepper); + NO_MOVE_SEMANTIC(SingleStepper); + + bool StepComplete(uint32_t bcOffset) const; + + private: + uint32_t GetStackDepth() const; + bool InStepRange(uint32_t pc) const; + + const EcmaVM *ecmaVm_; + JSMethod *method_; + CList stepRanges_; + uint32_t stackDepth_; + Type type_; + }; + + explicit PtJSExtractor(const File *pf) : DebugInfoExtractor(pf) {} + virtual ~PtJSExtractor() = default; + + template + bool MatchWithLine(const Callback &cb, int32_t line) + { + auto methods = GetMethodIdList(); + for (const auto &method : methods) { + auto table = GetLineNumberTable(method); + for (const auto &pair : table) { + if (static_cast(pair.line) == line) { + return cb(method, pair.offset); + } + } + } + return false; + } + + template + bool MatchWithOffset(const Callback &cb, File::EntityId methodId, uint32_t offset) + { + auto table = GetLineNumberTable(methodId); + panda_file::LineTableEntry prePair{0, 0}; + for (const auto &pair : table) { + if (offset < pair.offset) { + return cb(static_cast(prePair.line)); + } + if (offset == pair.offset) { + return cb(static_cast(pair.line)); + } + prePair = pair; + } + return cb(static_cast(table.back().line)); + } + + std::unique_ptr GetStepIntoStepper(const EcmaVM *ecmaVm); + std::unique_ptr GetStepOverStepper(const EcmaVM *ecmaVm); + std::unique_ptr GetStepOutStepper(const EcmaVM *ecmaVm); + +private: + NO_COPY_SEMANTIC(PtJSExtractor); + NO_MOVE_SEMANTIC(PtJSExtractor); + CList GetStepRanges(File::EntityId methodId, uint32_t offset); + std::unique_ptr GetStepper(const EcmaVM *ecmaVm, SingleStepper::Type type); +}; +} // namespace panda::tooling::ecmascript +#endif diff --git a/ecmascript/tooling/test/BUILD.gn b/ecmascript/tooling/test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..8b00c1fa1e909eab5c808f4a04e3e1b8b7e653ab --- /dev/null +++ b/ecmascript/tooling/test/BUILD.gn @@ -0,0 +1,214 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//ark/js_runtime/test/test_helper.gni") +import("//ark/ts2abc/ts2panda/ts2abc_config.gni") +import("//build/ohos.gni") +import("//build/test.gni") + +test_js_path = "//ark/js_runtime/ecmascript/tooling/test/js/Sample.js" +test_abc_path = "$target_out_dir/Sample.abc" + +module_output_path = "ark/js_runtime" + +config("debug_api_test") { + visibility = [ ":*" ] + + cflags_cc = [ "-Wno-gnu-zero-variadic-macro-arguments" ] + ldflags = [ "-Wl,-rpath=\$ORIGIN/" ] + include_dirs = [ + "//ark/js_runtime", + "$ark_root/third_party/libc_sec/include", + "//ark/js_runtime/ecmascript/tooling/test", + ] +} + +action("copy_resource_xml") { + visibility = [ ":*" ] # Only targets in this file can depend on this. + + deps = [] + + script = "//ark/js_runtime/test/copy_resource.py" + + src_path = "//ark/js_runtime/test/resource/js_runtime/" + src_xml = "ohos_test.xml" + dst_path = "//ark/test/resource/js_runtime/" + + args = [ + "--src-path", + rebase_path(src_path), + "--src-xml", + src_xml, + "--dst-path", + rebase_path(dst_path), + ] + + inputs = [ src_path + src_xml ] + outputs = [ "$target_out_dir" ] +} + +ts2abc_gen_abc("ark_debug_abc") { + extra_visibility = [ ":*" ] # Only targets in this file can depend on this. + src_js = rebase_path(test_js_path) + dst_file = rebase_path(test_abc_path) + extra_args = [ "--debug" ] + + in_puts = [ test_js_path ] + out_puts = [ test_abc_path ] +} + +ohos_shared_library("jsdebugtest") { + sources = [ + "init.cpp", + "test_extractor.cpp", + "test_list.cpp", + "test_util.cpp", + ] + + configs = [ + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + ":debug_api_test", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + "$ark_root/libpandafile:libarkfile", + "//ark/js_runtime:libark_jsruntime_static", + "//ark/js_runtime/ecmascript/tooling:libark_ecma_debugger_static", + ] + output_extension = "so" + relative_install_dir = "test" + subsystem_name = "test" +} + +host_unittest_action("EcmaDebugApiTest") { + module_out_path = module_output_path + + sources = [ + # test file + "launcher.cpp", + ] + + configs = [ + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + ":debug_api_test", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + ":ark_debug_abc", + ":copy_resource_xml", + ":jsdebugtest", + "$ark_root/libpandabase:libarkbase", + "$ark_root/libpandafile:libarkfile", + "//ark/js_runtime/ecmascript/tooling:libark_ecma_debugger_static", + ] +} + +host_unittest_action("DebuggerCommandsTest") { + module_out_path = module_output_path + + sources = [ + # test file + "debugger_commands_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + "//ark/js_runtime/ecmascript/tooling:libark_ecma_debugger_static", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("DebuggerEventsTest") { + module_out_path = module_output_path + + sources = [ + # test file + "debugger_events_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + "//ark/js_runtime/ecmascript/tooling:libark_ecma_debugger_static", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("DebuggerTypesTest") { + module_out_path = module_output_path + + sources = [ + # test file + "debugger_types_test.cpp", + ] + + configs = [ + "//ark/js_runtime:ecma_test_config", + "//ark/js_runtime:ark_jsruntime_public_config", # should add before + # arkruntime_public_config + "//ark/js_runtime:ark_jsruntime_config", + "$ark_root/runtime:arkruntime_public_config", + ] + + deps = [ + "$ark_root/libpandabase:libarkbase", + "//ark/js_runtime/ecmascript/tooling:libark_ecma_debugger_static", + sdk_libc_secshared_dep, + ] +} + +group("unittest") { + testonly = true + + # deps file + deps = [ + # ":DebuggerCommandsTest", + ":DebuggerEventsTest", + ":DebuggerTypesTest", + ":EcmaDebugApiTest", + ] +} + +group("host_unittest") { + testonly = true + + # deps file + deps = [ + # ":DebuggerCommandsTestAction(${host_toolchain})", + ":DebuggerEventsTestAction(${host_toolchain})", + ":DebuggerTypesTestAction(${host_toolchain})", + ] +} diff --git a/ecmascript/tooling/test/api_test.h b/ecmascript/tooling/test/api_test.h new file mode 100644 index 0000000000000000000000000000000000000000..1e9f5461539c17566ec74ff9c7d19e825ee1a1b2 --- /dev/null +++ b/ecmascript/tooling/test/api_test.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_DEBUG_TEST_API_TEST_H +#define PANDA_RUNTIME_DEBUG_TEST_API_TEST_H + +#include +#include "ecmascript/tooling/agent/js_backend.h" +#include "ecmascript/tooling/agent/js_pt_hooks.h" + +namespace panda::tooling::ecmascript::test { +using BreakpointCallback = std::function; +using LoadModuleCallback = std::function; +using PausedCallback = std::function; +using ExceptionCallback = std::function; +using SingleStepCallback = std::function; +using VmStartCallback = std::function; +using VmInitializationCallback = std::function; +using VmDeathCallback = std::function; +using MethodEntryCallback = std::function; +using Scenario = std::function; + +enum class DebugEvent { + BREAKPOINT, + LOAD_MODULE, + PAUSED, + EXCEPTION, + METHOD_ENTRY, + SINGLE_STEP, + VM_START, + VM_INITIALIZATION, + VM_DEATH, + UNINITIALIZED +}; + +std::ostream &operator<<(std::ostream &out, DebugEvent value); + +struct ApiTest { + BreakpointCallback breakpoint; + LoadModuleCallback load_module; + PausedCallback paused; + ExceptionCallback exception; + MethodEntryCallback method_entry; + SingleStepCallback single_step; + VmStartCallback vm_start; + VmInitializationCallback vm_init; + VmDeathCallback vm_death; + + Scenario scenario; + JSDebugger *debug_interface {nullptr}; + JSBackend *backend {nullptr}; + ApiTest(); + virtual ~ApiTest() = default; + + virtual std::pair GetEntryPoint() = 0; +}; +} // namespace panda::tooling::ecmascript::test + +#endif // PANDA_RUNTIME_DEBUG_TEST_API_TEST_H diff --git a/ecmascript/tooling/test/api_tests/api_tests.h b/ecmascript/tooling/test/api_tests/api_tests.h new file mode 100644 index 0000000000000000000000000000000000000000..d1a5773fb79abd9e72de73cecf0e0ea71e7ebb7a --- /dev/null +++ b/ecmascript/tooling/test/api_tests/api_tests.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Js +#include "js/js_breakpoint_test.h" +#include "js/js_single_step_test.h" diff --git a/ecmascript/tooling/test/api_tests/js/js_breakpoint_test.h b/ecmascript/tooling/test/api_tests/js/js_breakpoint_test.h new file mode 100644 index 0000000000000000000000000000000000000000..bb20e04af6529f0e8064caf8f2bf05d2305b0e60 --- /dev/null +++ b/ecmascript/tooling/test/api_tests/js/js_breakpoint_test.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_TEST_JS_BREAKPOINT_TEST_H +#define PANDA_TOOLING_TEST_JS_BREAKPOINT_TEST_H + +#include "ecmascript/tooling/test/test_util.h" +#include "ecmascript/mem/c_string.h" + +namespace panda::tooling::ecmascript::test { +class JsBreakpointTest : public ApiTest { +public: + JsBreakpointTest() + { + vm_start = [this] { + location_ = TestUtil::GetLocation("Sample.js", 7, panda_file_.c_str()); + location_.GetPandaFile(); + ASSERT_TRUE(location_.GetMethodId().IsValid()); + return true; + }; + + breakpoint = [this](PtThread ecmaVm, const PtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, location_); + ++breakpoint_counter_; + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, ecmaVm, location); + return true; + }; + + load_module = [this](std::string_view moduleName) { + if (flag_) { + if (moduleName.find(panda_file_.c_str()) == std::string_view::npos) { + return true; + } + ASSERT_TRUE(backend->NotifyScriptParsed(0, panda_file_)); + flag_ = 0; + location_.GetPandaFile(); + ASSERT_SUCCESS(debug_interface->SetBreakpoint(location_)); + auto error = debug_interface->SetBreakpoint(location_); + ASSERT_FALSE(!error); + } + return true; + }; + + scenario = [this]() { + ASSERT_BREAKPOINT_SUCCESS(location_); + ASSERT_SUCCESS(debug_interface->RemoveBreakpoint(location_)); + TestUtil::Continue(); + ASSERT_EXITED(); + return true; + }; + + vm_death = [this]() { + ASSERT_EQ(breakpoint_counter_, 1U); + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_, entry_point_}; + } + + ~JsBreakpointTest() = default; + +private: + CString panda_file_ = "/data/app/Sample.abc"; + CString entry_point_ = "_GLOBAL::func_main_0"; + PtLocation location_ {nullptr, PtLocation::EntityId(0), 0}; + size_t breakpoint_counter_ = 0; + bool flag_ = 1; +}; + +std::unique_ptr GetJsBreakpointTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::ecmascript::test + +#endif // PANDA_TOOLING_TEST_JS_BREAKPOINT_TEST_H diff --git a/ecmascript/tooling/test/api_tests/js/js_single_step_test.h b/ecmascript/tooling/test/api_tests/js/js_single_step_test.h new file mode 100644 index 0000000000000000000000000000000000000000..9de0f6396bb59cf9701eb47b0a8a6c01b45b5b75 --- /dev/null +++ b/ecmascript/tooling/test/api_tests/js/js_single_step_test.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_TOOLING_TEST_JS_SINGLE_STEP_TEST_H +#define PANDA_TOOLING_TEST_JS_SINGLE_STEP_TEST_H + +#include "ecmascript/tooling/test/test_util.h" + +namespace panda::tooling::ecmascript::test { +class JsSingleStepTest : public ApiTest { +public: + JsSingleStepTest() + { + vm_start = [this] { + location_start_ = TestUtil::GetLocation("Sample.js", 4, panda_file_.c_str()); + location_end_ = TestUtil::GetLocation("Sample.js", 7, panda_file_.c_str()); + return true; + }; + + vm_death = [this]() { + ASSERT_NE(step_count_, 0); + ASSERT_EQ(breakpoint_count_, 2); + return true; + }; + + load_module = [this](std::string_view moduleName) { + if (flag) { + if (moduleName.find(panda_file_.c_str()) == std::string_view::npos) { + return true; + } + flag = 0; + ASSERT_SUCCESS(debug_interface->SetBreakpoint(location_end_)); + } + return true; + }; + + breakpoint = [this](PtThread, const PtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, location_end_); + // Check what step signalled before breakpoint + ASSERT_LOCATION_EQ(location, location_step_); + ASSERT_TRUE(collect_steps_); + breakpoint_count_++; + // Disable collect steps + collect_steps_ = false; + return true; + }; + + single_step = [this](PtThread, const PtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + if (!collect_steps_) { + if (location_start_ == location) { + collect_steps_ = true; + } + return true; + } + + ASSERT_NE(bytecode_offset_, location.GetBytecodeOffset()); + location_step_ = location; + step_count_++; + bytecode_offset_ = location.GetBytecodeOffset(); + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_, entry_point_}; + } + +private: + CString panda_file_ = "/data/app/Sample.abc"; + CString entry_point_ = "_GLOBAL::func_main_0"; + PtLocation location_start_{nullptr, PtLocation::EntityId(0), 0}; + PtLocation location_end_{nullptr, PtLocation::EntityId(0), 0}; + PtLocation location_step_{nullptr, PtLocation::EntityId(0), 0}; + int32_t step_count_ = 0; + int32_t breakpoint_count_ = 0; + bool collect_steps_ = false; + uint32_t bytecode_offset_ = std::numeric_limits::max(); + bool flag = 1; +}; + +std::unique_ptr GetJsSingleStepTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::ecmascript::test + +#endif // PANDA_TOOLING_TEST_JS_SINGLE_STEP_TEST_H diff --git a/ecmascript/tooling/test/debugger_commands_test.cpp b/ecmascript/tooling/test/debugger_commands_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1a04e912e998428e97f88dd57d7e7a0557e939f0 --- /dev/null +++ b/ecmascript/tooling/test/debugger_commands_test.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_array.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/tooling/base/pt_params.h" +#include "ecmascript/tooling/base/pt_returns.h" +#include "ecmascript/tooling/debugger_service.h" +#include "ecmascript/tooling/dispatcher.h" +#include "ecmascript/tooling/interface/js_debugger.h" + +using namespace panda::ecmascript; +using namespace panda::tooling::ecmascript; +using namespace panda::tooling; + +namespace panda::test { + +class DebuggerCommandsTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + ecmaVm = EcmaVM::Cast(instance); + // Main logic is JSON parser, so not need trigger GC to decrease execute time + ecmaVm->GetFactory()->SetTriggerGc(false); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(ecmaVm, scope); + } + +protected: + EcmaVM *ecmaVm {nullptr}; + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(DebuggerCommandsTest, CreateDebuggerTest) +{ + std::unique_ptr debugger = std::make_unique(Runtime::GetCurrent(), ecmaVm); + ASSERT_NE(debugger, nullptr); +} +} // namespace panda::test \ No newline at end of file diff --git a/ecmascript/tooling/test/debugger_events_test.cpp b/ecmascript/tooling/test/debugger_events_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11f6615f22016859359afa8be8afac4fa68aa67f --- /dev/null +++ b/ecmascript/tooling/test/debugger_events_test.cpp @@ -0,0 +1,915 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_array.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/tooling/base/pt_events.h" +#include "ecmascript/tooling/base/pt_types.h" +#include "ecmascript/tooling/dispatcher.h" + +using namespace panda::ecmascript; +using namespace panda::tooling::ecmascript; +using ObjectType = RemoteObject::TypeName; +using ObjectSubType = RemoteObject::SubTypeName; +using ObjectClassName = RemoteObject::ClassName; + +namespace panda::test { + +class DebuggerEventsTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + ecmaVm = EcmaVM::Cast(instance); + // Main logic is JSON parser, so not need trigger GC to decrease execute time + ecmaVm->GetFactory()->SetTriggerGc(false); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(ecmaVm, scope); + } + +protected: + EcmaVM *ecmaVm {nullptr}; + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(DebuggerEventsTest, BreakpointResolvedCreateTest) +{ + CString msg; + std::unique_ptr breakpointResolved; + + // abnormal params of null msg + msg = CString() + R"({})"; + breakpointResolved = BreakpointResolved::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakpointResolved, nullptr); + + // abnormal params of unexist key params + msg = CString() + R"({"id":0,"method":"Debugger.Test"})"; + breakpointResolved = BreakpointResolved::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakpointResolved, nullptr); + + // abnormal params of null params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + breakpointResolved = BreakpointResolved::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakpointResolved, nullptr); + + // abnormal params of unknown params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + breakpointResolved = BreakpointResolved::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakpointResolved, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"breakpointId":"00"}})"; + breakpointResolved = BreakpointResolved::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakpointResolved, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"location":{"scriptId":"id2","lineNumber":99}}})"; + breakpointResolved = BreakpointResolved::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakpointResolved, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"breakpointId":"00", + "location":{"scriptId":"id2","lineNumber":99}}})"; + breakpointResolved = BreakpointResolved::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(breakpointResolved, nullptr); +} + +HWTEST_F_L0(DebuggerEventsTest, BreakpointResolvedToObjectTest) +{ + CString msg; + std::unique_ptr breakpointResolved; + Local tmpStr = StringRef::NewFromUtf8(ecmaVm, "params"); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"breakpointId":"00", + "location":{"scriptId":"id2","lineNumber":99}}})"; + breakpointResolved = BreakpointResolved::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + + ASSERT_NE(breakpointResolved, nullptr); + Local object1 = breakpointResolved->ToObject(ecmaVm); + Local result = object1->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + Local object = Local(result); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "breakpointId"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("00", Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "location"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); +} + +HWTEST_F_L0(DebuggerEventsTest, PausedCreateTest) +{ + CString msg; + std::unique_ptr paused; + + // abnormal params of null msg + msg = CString() + R"({})"; + paused = Paused::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(paused, nullptr); + + // abnormal params of unexist key params + msg = CString() + R"({"id":0,"method":"Debugger.Test"})"; + paused = Paused::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(paused, nullptr); + + // abnormal params of null params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + paused = Paused::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(paused, nullptr); + + // abnormal params of unknown params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + paused = Paused::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(paused, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"reason":"exception"}})"; + paused = Paused::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(paused, nullptr); + + msg = CString() + + R"({"id":0,"method":"Debugger.Test","params": + {"callFrames":[)" + + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":10,"functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})" + + R"(]}})"; + paused = Paused::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(paused, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"callFrames":[)" + + R"({"callFrameId":"10","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}})" + + R"(],"reason":"exception"}})"; + paused = Paused::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(paused, nullptr); +} + +HWTEST_F_L0(DebuggerEventsTest, PausedToObjectTest) +{ + CString msg; + std::unique_ptr paused; + Local tmpStr = StringRef::NewFromUtf8(ecmaVm, "params"); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"callFrames":[{"callFrameId":"10","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7", + "scopeChain":[{"type":"global","object":{"type":"object"}}, {"type":"local","object":{"type":"object"}}], + "this":{"type":"object","subtype":"v128"}}],"reason":"exception"}})"; + paused = Paused::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(paused, nullptr); + + Local object1 = paused->ToObject(ecmaVm); + Local result = object1->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + Local object = Local(result); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "reason"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("exception", Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "callFrames"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsArray(ecmaVm)); +} + +HWTEST_F_L0(DebuggerEventsTest, ResumedToObjectTest) +{ + CString msg; + std::unique_ptr resumed = std::make_unique(); + Local tmpStr; + + Local object = resumed->ToObject(ecmaVm); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "method"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + Local result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(std::string(resumed->GetName().c_str()), Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "params"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); +} + +HWTEST_F_L0(DebuggerEventsTest, ScriptFailedToParseCreateTest) +{ + CString msg; + std::unique_ptr parse; + + // abnormal params of null msg + msg = CString() + R"({})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + // abnormal params of unexist key params + msg = CString() + R"({"id":0,"method":"Debugger.Test"})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + // abnormal params of null params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + // abnormal params of unknown params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2}})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn:"10}})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10}})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4}})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0}})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js"}})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00"}})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001"}})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001", + "executionContextAuxData":{"a":1}, + "sourceMapURL":"usr/", + "hasSourceURL":true, + "isModule":true, + "length":34, + "stackTrace":{"callFrames":[{"callFrameId":"10","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7", + "scopeChain":[{"type":"global","object":{"type":"object"}}, {"type":"local","object":{"type":"object"}}], + "this":{"type":"object","subtype":"v128"}}]}, + "codeOffset":432, + "scriptLanguage":"JavaScript" + }})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001", + "executionContextAuxData":{"a":1}, + "sourceMapURL":"usr/", + "hasSourceURL":true, + "isModule":true, + "length":34, + "stackTrace":{"callFrames":[{"callFrameId":"10","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7", + "scopeChain":[{"type":"global","object":{"type":"object"}}, {"type":"local","object":{"type":"object"}}], + "this":{"type":"object","subtype":"v128"}}]}, + "codeOffset":432 + }})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001", + "executionContextAuxData":{"a":1}, + "sourceMapURL":"usr/", + "hasSourceURL":true, + "isModule":true, + "length":34, + "stackTrace":{"callFrames":[{"callFrameId":"10","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7", + "scopeChain":[{"type":"global","object":{"type":"object"}}, {"type":"local","object":{"type":"object"}}], + "this":{"type":"object","subtype":"v128"}}]} + }})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001", + "executionContextAuxData":{"a":1}, + "sourceMapURL":"usr/", + "hasSourceURL":true, + "isModule":true, + "length":34 + }})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001", + "executionContextAuxData":{"a":1}, + "sourceMapURL":"usr/", + "hasSourceURL":true, + "isModule":true + }})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001", + "executionContextAuxData":{"a":1}, + "sourceMapURL":"usr/", + "hasSourceURL":true + }})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001", + "executionContextAuxData":{"a":1}, + "sourceMapURL":"usr/" + }})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001", + "executionContextAuxData":{"a":1} + }})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001" + }})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001", + "executionContextAuxData":{"a":1}, + "sourceMapURL":"usr/", + "hasSourceURL":true, + "isModule":true, + "length":34, + "stackTrace":{"callFrames":[{"callFrameId":"10","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7", + "scopeChain":[{"type":"global","object":{"type":"object"}}, {"type":"local","object":{"type":"object"}}], + "this":{"type":"object","subtype":"v128"}}]}, + "codeOffset":432, + "scriptLanguage":"JavaScript", + "embedderName":"hh" + }})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); +} + +HWTEST_F_L0(DebuggerEventsTest, ScriptFailedToParseToObjectTest) +{ + CString msg; + std::unique_ptr parse; + Local tmpStr = StringRef::NewFromUtf8(ecmaVm, "params"); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001", + "executionContextAuxData":{"a":1}, + "sourceMapURL":"usr/", + "hasSourceURL":true, + "isModule":true, + "length":34, + "stackTrace":{"callFrames":[{"callFrameId":"10","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7", + "scopeChain":[{"type":"global","object":{"type":"object"}}, {"type":"local","object":{"type":"object"}}], + "this":{"type":"object","subtype":"v128"}}]}, + "codeOffset":432, + "scriptLanguage":"JavaScript", + "embedderName":"hh" + }})"; + parse = ScriptFailedToParse::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); + + Local object1 = parse->ToObject(ecmaVm); + Local result = object1->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + Local object = Local(result); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "scriptId"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("00", Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "url"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("use/test.js", Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "startLine"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 0); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "startColumn"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 4); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "endLine"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 10); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "endColumn"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 10); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "executionContextId"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 2); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "hash"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("hash0001", Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "executionContextAuxData"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "sourceMapURL"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("usr/", Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "hasSourceURL"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsTrue()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "isModule"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsTrue()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "length"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 34); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "codeOffset"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 432); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "scriptLanguage"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("JavaScript", Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "embedderName"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("hh", Local(result)->ToString()); +} + +HWTEST_F_L0(DebuggerEventsTest, ScriptParsedCreateTest) +{ + CString msg; + std::unique_ptr parse; + + // abnormal params of null msg + msg = CString() + R"({})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + // abnormal params of unexist key params + msg = CString() + R"({"id":0,"method":"Debugger.Test"})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + // abnormal params of null params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + // abnormal params of unknown params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2}})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn:"10}})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10}})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4}})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0}})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js"}})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00"}})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001"}})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(parse, nullptr); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001", + "executionContextAuxData":{"a":1}, + "isLiveEdit":true, + "sourceMapURL":"usr/", + "hasSourceURL":true, + "isModule":true, + "length":34, + "stackTrace":{"callFrames":[{"callFrameId":"10","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7", + "scopeChain":[{"type":"global","object":{"type":"object"}}, {"type":"local","object":{"type":"object"}}], + "this":{"type":"object","subtype":"v128"}}]}, + "codeOffset":432, + "scriptLanguage":"JavaScript", + "embedderName":"hh" + }})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); +} + +HWTEST_F_L0(DebuggerEventsTest, ScriptParsedToObjectTest) +{ + CString msg; + std::unique_ptr parse; + Local tmpStr = StringRef::NewFromUtf8(ecmaVm, "params"); + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params": + {"scriptId":"00", + "url":"use/test.js", + "startLine":0, + "startColumn":4, + "endLine":10, + "endColumn":10, + "executionContextId":2, + "hash":"hash0001", + "executionContextAuxData":{"a":1}, + "isLiveEdit":true, + "sourceMapURL":"usr/", + "hasSourceURL":true, + "isModule":true, + "length":34, + "stackTrace":{"callFrames":[{"callFrameId":"10","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7", + "scopeChain":[{"type":"global","object":{"type":"object"}}, {"type":"local","object":{"type":"object"}}], + "this":{"type":"object","subtype":"v128"}}]}, + "codeOffset":432, + "scriptLanguage":"JavaScript", + "embedderName":"hh" + }})"; + parse = ScriptParsed::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(parse, nullptr); + + Local object1 = parse->ToObject(ecmaVm); + Local result = object1->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + Local object = Local(result); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "scriptId"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("00", Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "url"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("use/test.js", Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "startLine"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 0); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "startColumn"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 4); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "endLine"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 10); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "endColumn"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 10); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "executionContextId"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 2); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "hash"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("hash0001", Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "executionContextAuxData"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "isLiveEdit"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsTrue()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "sourceMapURL"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("usr/", Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "hasSourceURL"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsTrue()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "isModule"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsTrue()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "length"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 34); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "codeOffset"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 432); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "scriptLanguage"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("JavaScript", Local(result)->ToString()); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "embedderName"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("hh", Local(result)->ToString()); +} + +} // namespace panda::test \ No newline at end of file diff --git a/ecmascript/tooling/test/debugger_types_test.cpp b/ecmascript/tooling/test/debugger_types_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6818eac7732b0bda31b9bde8d5e31d974ab71f01 --- /dev/null +++ b/ecmascript/tooling/test/debugger_types_test.cpp @@ -0,0 +1,1784 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/js_array.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/tooling/base/pt_types.h" +#include "ecmascript/tooling/dispatcher.h" + +using namespace panda::ecmascript; +using namespace panda::tooling::ecmascript; + +namespace panda::test { + +// Duplicate name of panda::ecmascript::PropertyDescriptor in js_object-inl.h +using panda::tooling::ecmascript::PropertyDescriptor; + +using ObjectType = RemoteObject::TypeName; +using ObjectSubType = RemoteObject::SubTypeName; +using ObjectClassName = RemoteObject::ClassName; + +class DebuggerTypesTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + ecmaVm = EcmaVM::Cast(instance); + // Main logic is JSON parser, so not need trigger GC to decrease execute time + ecmaVm->GetFactory()->SetTriggerGc(false); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + +protected: + EcmaVM *ecmaVm {nullptr}; + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +HWTEST_F_L0(DebuggerTypesTest, RemoteObjectCreateTest) +{ + CString msg; + std::unique_ptr remoteObject; + Local tmpStr; + + // abnormal params of null msg + msg = CString() + R"({})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of unexist key params + msg = CString() + R"({"id":0,"method":"Debugger.Test"})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of null params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of unknown params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = 100, ] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":100}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = [ "sub": "test" ] }, ] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":100}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // normal params of params.sub-key = [ type = "object", ] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + R"("}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + + // abnormal params of params.sub-key = [ type = "object", subtype = "unknown"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","subtype":"unknown"}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = "object", subtype = 100] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","subtype":100}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // normal params of params.sub-key = [ type = "object", subtype = "array"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","subtype":")" + ObjectSubType::Array + R"("}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasSubType()); + EXPECT_EQ(ObjectSubType::Array, remoteObject->GetSubType()); + + // abnormal params of params.sub-key = [ type = "object", className = 100] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","className":100}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = "object", className = {"xx":"yy"}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","className":{"xx":"yy"}}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // normal params of params.sub-key = [ type = "object", className = "TestClass"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","className":"TestClass"}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasClassName()); + EXPECT_EQ("TestClass", remoteObject->GetClassName()); + + // normal params of params.sub-key = [ type = "object", value = 100] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","value":100}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasValue()); + EXPECT_EQ(Local(remoteObject->GetValue())->Value(), 100.0); + + // normal params of params.sub-key = [ type = "object", value = {"xx":"yy"}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","value":{"xx":"yy"}}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasValue()); + ASSERT_TRUE(remoteObject->GetValue()->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "xx"); + ASSERT_TRUE(Local(remoteObject->GetValue())->Has(ecmaVm, Local(tmpStr))); + + // normal params of params.sub-key = [ type = "object", value = "Test"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","value":"Test"}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasValue()); + EXPECT_EQ("Test", Local(remoteObject->GetValue())->ToString()); + + // abnormal params of params.sub-key = [ type = "object", unserializableValue = 100] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","unserializableValue":100}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = "object", unserializableValue = {"xx":"yy"}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","unserializableValue":{"xx":"yy"}}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // normal params of params.sub-key = [ type = "object", unserializableValue = "TestClass"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","unserializableValue":"Test"}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasUnserializableValue()); + EXPECT_EQ("Test", remoteObject->GetUnserializableValue()); + + // abnormal params of params.sub-key = [ type = "object", description = 100] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","description":100}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = "object", description = {"xx":"yy"}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","description":{"xx":"yy"}}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // normal params of params.sub-key = [ type = "object", description = "Test"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","description":"Test"}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasDescription()); + EXPECT_EQ("Test", remoteObject->GetDescription()); + + // abnormal params of params.sub-key = [ type = "object", objectId = 100] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","objectId":100}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // abnormal params of params.sub-key = [ type = "object", objectId = {"xx":"yy"}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","objectId":{"xx":"yy"}}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(remoteObject, nullptr); + + // normal params of params.sub-key = [ type = "object", objectId = "id_1"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + + R"(","objectId":"id_1"}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + EXPECT_EQ(ObjectType::Object, remoteObject->GetType()); + ASSERT_TRUE(remoteObject->HasObjectId()); + EXPECT_EQ("id_1", remoteObject->GetObjectId()); +} + +HWTEST_F_L0(DebuggerTypesTest, RemoteObjectToObjectTest) +{ + CString msg; + std::unique_ptr remoteObject; + Local tmpStr; + + msg = + CString() + R"({"id":0,"method":"Debugger.Test","params":{"type":")" + ObjectType::Object + R"(","subtype":")" + + ObjectSubType::Array + + R"(","className":"TestClass","value":100,"unserializableValue":"Test","description":"Test","objectId":"id_1"}})"; + remoteObject = RemoteObject::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(remoteObject, nullptr); + Local object = remoteObject->ToObject(ecmaVm); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "type"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + Local result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "subtype"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(std::string(ObjectSubType::Array.c_str()), Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "className"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("TestClass", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "value"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 100.0); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "unserializableValue"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("Test", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "description"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("Test", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "objectId"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("id_1", Local(result)->ToString()); +} + +HWTEST_F_L0(DebuggerTypesTest, ExceptionDetailsCreateTest) +{ + CString msg; + std::unique_ptr exceptionMetaData; + + // abnormal params of null msg + msg = CString() + R"({})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of unexist key params + msg = CString() + R"({"id":0,"method":"Debugger.Test"})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of null params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of unknown params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId="Test","text"="text0","lineNumber"=10,"columnNumber"=20] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":"Test","text":"text0","lineNumber":10,"columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId={"xx":"yy"},"text"="text0","lineNumber"=10,"columnNumber"=20] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":{"xx":"yy"},"text":"text0","lineNumber":10,"columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId=3,"text"=10,"lineNumber"=10,"columnNumber"=20] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":10,"lineNumber":10,"columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId=3,"text"=["text0"],"lineNumber"=10,"columnNumber"=20] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":["text0"],"lineNumber":10,"columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId=3,"text"="text0","lineNumber"="10","columnNumber"=20] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":"10","columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId=3,"text"="text0","lineNumber"=["10"],"columnNumber"=20] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":["10"],"columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"="20"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":"20"}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = [ exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=["20"]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":["20"]}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // normal params of params.sub-key = [ exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(exceptionMetaData, nullptr); + EXPECT_EQ(exceptionMetaData->GetExceptionId(), 3); + EXPECT_EQ("text0", exceptionMetaData->GetText()); + EXPECT_EQ(exceptionMetaData->GetLine(), 10); + EXPECT_EQ(exceptionMetaData->GetColumn(), 20); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"scriptId"=10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"scriptId":10}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"scriptId"=["10"]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"scriptId":["10"]}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // normal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"scriptId"="id0"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"scriptId":"id0"}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(exceptionMetaData, nullptr); + EXPECT_EQ(exceptionMetaData->GetExceptionId(), 3); + EXPECT_EQ("text0", exceptionMetaData->GetText()); + EXPECT_EQ(exceptionMetaData->GetLine(), 10); + EXPECT_EQ(exceptionMetaData->GetColumn(), 20); + EXPECT_EQ("id0", exceptionMetaData->GetScriptId()); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"url"=10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"url":10}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"url"=["10"]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"url":["10"]}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // normal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"url"="url0"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"url":"url0"}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(exceptionMetaData, nullptr); + EXPECT_EQ(exceptionMetaData->GetExceptionId(), 3); + EXPECT_EQ("text0", exceptionMetaData->GetText()); + EXPECT_EQ(exceptionMetaData->GetLine(), 10); + EXPECT_EQ(exceptionMetaData->GetColumn(), 20); + EXPECT_EQ("url0", exceptionMetaData->GetUrl()); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"exception"=10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"exception":10}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"exception"=["10"]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"exception":["10"]}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // normal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"exception"={}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"exception":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Error + R"("}}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(exceptionMetaData, nullptr); + EXPECT_EQ(exceptionMetaData->GetExceptionId(), 3); + EXPECT_EQ("text0", exceptionMetaData->GetText()); + EXPECT_EQ(exceptionMetaData->GetLine(), 10); + EXPECT_EQ(exceptionMetaData->GetColumn(), 20); + RemoteObject *exception = exceptionMetaData->GetException(); + ASSERT_NE(exception, nullptr); + EXPECT_EQ(exception->GetType(), ObjectType::Object); + EXPECT_EQ(exception->GetSubType(), ObjectSubType::Error); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"executionContextId"="10"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"executionContextId":"10"}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // abnormal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"executionContextId"=["10"]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"executionContextId":["10"]}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(exceptionMetaData, nullptr); + + // normal params of params.sub-key = + // [exceptionId=3,"text"="text0","lineNumber"=10,"columnNumber"=20,"executionContextId"=2] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":3,"text":"text0","lineNumber":10,"columnNumber":20,"executionContextId":2}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(exceptionMetaData, nullptr); + EXPECT_EQ(exceptionMetaData->GetExceptionId(), 3); + EXPECT_EQ("text0", exceptionMetaData->GetText()); + EXPECT_EQ(exceptionMetaData->GetLine(), 10); + EXPECT_EQ(exceptionMetaData->GetColumn(), 20); + EXPECT_EQ(exceptionMetaData->GetExecutionContextId(), 2); +} + +HWTEST_F_L0(DebuggerTypesTest, ExceptionDetailsToObjectTest) +{ + CString msg; + std::unique_ptr exceptionMetaData; + Local tmpStr; + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "exceptionId":5,"text":"text0","lineNumber":10,"columnNumber":20,"scriptId":"ScriptId0","url":"url0", + "exception":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Error + R"("},"executionContextId":30}})"; + exceptionMetaData = ExceptionDetails::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(exceptionMetaData, nullptr); + Local object = exceptionMetaData->ToObject(ecmaVm); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "exceptionId"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + Local result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 5); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "text"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("text0", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "lineNumber"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 10); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "columnNumber"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 20); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "scriptId"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("ScriptId0", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "url"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("url0", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "exception"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "type"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + Local subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "subtype"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectSubType::Error.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "executionContextId"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 30); +} + +HWTEST_F_L0(DebuggerTypesTest, InternalPropertyDescriptorCreateTest) +{ + CString msg; + std::unique_ptr internalPropertyDescriptor; + + // abnormal params of null msg + msg = CString() + R"({})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unexist key params + msg = CString() + R"({"id":0,"method":"Debugger.Test"})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of null params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key=["name":"name8"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":99}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key=["name":"name8"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":99}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key=["name":"name8","value":99] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":99}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key=["name":99] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":99}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key=["name":[99]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":[99]}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name7"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name7"}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(internalPropertyDescriptor, nullptr); + EXPECT_EQ("name7", internalPropertyDescriptor->GetName()); + + // abnormal params of unknown params.sub-key=["name":"name8","value":{"type":"object","subtype":"map"}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":"99"}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key=["name":"name8","value":{"type":"object","subtype":"wrong"}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":{"type":")" + + ObjectType::Object + R"(","subtype":"wrong"}}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(internalPropertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","value":{"type":"object","subtype":"map"}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Map + R"("}}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(internalPropertyDescriptor, nullptr); + EXPECT_EQ("name8", internalPropertyDescriptor->GetName()); + ASSERT_TRUE(internalPropertyDescriptor->HasValue()); + RemoteObject *value = internalPropertyDescriptor->GetValue(); + ASSERT_NE(value, nullptr); + EXPECT_EQ(value->GetType(), ObjectType::Object); + EXPECT_EQ(value->GetSubType(), ObjectSubType::Map); +} + +HWTEST_F_L0(DebuggerTypesTest, InternalPropertyDescriptorToObjectTest) +{ + CString msg; + std::unique_ptr internalPropertyDescriptor; + Local tmpStr; + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Map + R"("}}})"; + internalPropertyDescriptor = InternalPropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(internalPropertyDescriptor, nullptr); + Local object = internalPropertyDescriptor->ToObject(ecmaVm); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "name"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + Local result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("name8", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "value"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "type"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + Local subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "subtype"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectSubType::Map.c_str()), Local(subResult)->ToString()); +} + +HWTEST_F_L0(DebuggerTypesTest, PropertyDescriptorCreateTest) +{ + CString msg; + std::unique_ptr propertyDescriptor; + + // abnormal params of null msg + msg = CString() + R"({})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of unexist key params + msg = CString() + R"({"id":0,"method":"Debugger.Test"})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of null params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of unknown params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":10,"configurable":true,"enumerable":true] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":10,"configurable":true,"enumerable":true,"value":10}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":["name85"],"configurable":true,"enumerable":true] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":["name85"],"configurable":true,"enumerable":true,"value":10}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":10,"enumerable":true] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":10,"enumerable":true}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":"true","enumerable":true] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":"true","enumerable":true}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":10}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":"true"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":"true"}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"value":10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"value":10}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"value":{"ee":"11"}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"value":{"ee":"11"}}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"value":{..}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"value":{"type":")" + + ObjectType::Symbol + R"("}}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + RemoteObject *value = propertyDescriptor->GetValue(); + ASSERT_NE(value, nullptr); + EXPECT_EQ(value->GetType(), ObjectType::Symbol); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"writable":98] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"writable":98}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"writable":[true]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"writable":[true]}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"writable":true] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"writable":true}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + ASSERT_TRUE(propertyDescriptor->GetWritable()); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"get":10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"get":10}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"get":[10]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"get":[10]}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"get":{}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"get":{"type":")" + + ObjectType::Function + R"("}}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + RemoteObject *get = propertyDescriptor->GetGet(); + ASSERT_NE(get, nullptr); + EXPECT_EQ(get->GetType(), ObjectType::Function); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"set":10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"set":10}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"set":[10]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"set":[10]}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"set":{}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"set":{"type":")" + + ObjectType::String + R"("}}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + RemoteObject *set = propertyDescriptor->GetSet(); + ASSERT_NE(set, nullptr); + EXPECT_EQ(set->GetType(), ObjectType::String); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"wasThrown":98] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"wasThrown":98}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"wasThrown":[true]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"wasThrown":[true]}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"wasThrown":true] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"wasThrown":true}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + ASSERT_TRUE(propertyDescriptor->GetWasThrown()); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"isOwn":98] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"isOwn":98}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"isOwn":[true]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"isOwn":[true]}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true,"isOwn":true] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"isOwn":true}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + ASSERT_TRUE(propertyDescriptor->GetIsOwn()); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true, "symbol":10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"symbol":10}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // abnormal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true, "symbol":[10]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"symbol":[10]}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(propertyDescriptor, nullptr); + + // normal params of params.sub-key=["name":"name8","configurable":true,"enumerable":true, "symbol":{}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name85","configurable":true,"enumerable":true,"symbol":{"type":")" + + ObjectType::Wasm + R"("}}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + EXPECT_EQ("name85", propertyDescriptor->GetName()); + ASSERT_TRUE(propertyDescriptor->GetConfigurable()); + ASSERT_TRUE(propertyDescriptor->GetEnumerable()); + RemoteObject *symbol = propertyDescriptor->GetSymbol(); + ASSERT_NE(symbol, nullptr); + EXPECT_EQ(symbol->GetType(), ObjectType::Wasm); +} + +HWTEST_F_L0(DebuggerTypesTest, PropertyDescriptorToObjectTest) +{ + CString msg; + std::unique_ptr propertyDescriptor; + Local tmpStr; + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "name":"name8","value":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Map + R"("}, + "writable":true,"get":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Regexp + R"("},"set":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Generator + + R"("},"configurable":true,"enumerable":true,"wasThrown":true,"isOwn":true,"symbol":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Proxy + R"("}}})"; + propertyDescriptor = PropertyDescriptor::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(propertyDescriptor, nullptr); + Local object = propertyDescriptor->ToObject(ecmaVm); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "name"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + Local result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("name8", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "value"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "type"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + Local subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "subtype"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectSubType::Map.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "writable"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsTrue()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "get"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "type"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "subtype"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectSubType::Regexp.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "set"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "type"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "subtype"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectSubType::Generator.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "configurable"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsTrue()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "enumerable"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsTrue()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "wasThrown"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsTrue()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "isOwn"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsTrue()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "symbol"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "type"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "subtype"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectSubType::Proxy.c_str()), Local(subResult)->ToString()); +} + +HWTEST_F_L0(DebuggerTypesTest, LocationCreateTest) +{ + CString msg; + std::unique_ptr location; + + // abnormal params of null msg + msg = CString() + R"({})"; + location = Location::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of unexist key params + msg = CString() + R"({"id":0,"method":"Debugger.Test"})"; + location = Location::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of null params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + location = Location::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of unknown params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + location = Location::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of params.sub-key=["scriptId":10,"lineNumber":99] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":10,"lineNumber":99 + }})"; + location = Location::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of params.sub-key=["scriptId":["id3"],"lineNumber":99] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":["id3"],"lineNumber":99 + }})"; + location = Location::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of params.sub-key=["scriptId":"id222","lineNumber":"99"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id222","lineNumber":"99" + }})"; + location = Location::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of params.sub-key=["scriptId":"id222","lineNumber":[99]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id222","lineNumber":[99] + }})"; + location = Location::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // abnormal params of params.sub-key=["scriptId":"id2","lineNumber":99,"columnNumber":"18"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id222","lineNumber":899,"columnNumber":"18" + }})"; + location = Location::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(location, nullptr); + + // normal params of params.sub-key=["scriptId":"id2","lineNumber":99,"columnNumber":138] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id222","lineNumber":899,"columnNumber":138 + }})"; + location = Location::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(location, nullptr); + EXPECT_EQ("id222", location->GetScriptId()); + EXPECT_EQ(location->GetLine(), 899); + EXPECT_EQ(location->GetColumn(), 138); + + // normal params of params.sub-key=["scriptId":"id2122","lineNumber":8299] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id2122","lineNumber":8299 + }})"; + location = Location::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(location, nullptr); + EXPECT_EQ("id2122", location->GetScriptId()); + EXPECT_EQ(location->GetLine(), 8299); +} + +HWTEST_F_L0(DebuggerTypesTest, LocationToObjectTest) +{ + CString msg; + std::unique_ptr location; + Local tmpStr; + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id2","lineNumber":99,"columnNumber":18 + }})"; + location = Location::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(location, nullptr); + Local object = location->ToObject(ecmaVm); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "scriptId"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + Local result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("id2", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "lineNumber"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 99); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "columnNumber"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 18); +} + +HWTEST_F_L0(DebuggerTypesTest, BreakLocationCreateTest) +{ + CString msg; + std::unique_ptr breakLocation; + + // abnormal params of null msg + msg = CString() + R"({})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of unexist key params + msg = CString() + R"({"id":0,"method":"Debugger.Test"})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of null params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of unknown params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":10,"lineNumber":99] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":10,"lineNumber":99 + }})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":["id3"],"lineNumber":99] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":["id3"],"lineNumber":99 + }})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":"id222","lineNumber":"99"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id222","lineNumber":"99" + }})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":"id222","lineNumber":[99]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id222","lineNumber":[99] + }})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":"id2","lineNumber":99,"columnNumber":"18"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id222","lineNumber":899,"columnNumber":"18" + }})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":"id2","lineNumber":99,"columnNumber":"18","type":10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id222","lineNumber":899,"columnNumber":"18","type":10 + }})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // abnormal params of params.sub-key=["scriptId":"id2","lineNumber":99,"columnNumber":"18","type":"ee"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id222","lineNumber":899,"columnNumber":"18","type":"ee" + }})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(breakLocation, nullptr); + + // normal params of params.sub-key=["scriptId":"id2","lineNumber":99,"columnNumber":138,"type":"return"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id222","lineNumber":899,"columnNumber":138,"type":"return" + }})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(breakLocation, nullptr); + EXPECT_EQ("id222", breakLocation->GetScriptId()); + EXPECT_EQ(breakLocation->GetLine(), 899); + EXPECT_EQ(breakLocation->GetColumn(), 138); + EXPECT_EQ("return", breakLocation->GetType()); + + // normal params of params.sub-key=["scriptId":"id2122","lineNumber":8299] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id2122","lineNumber":8299 + }})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(breakLocation, nullptr); + EXPECT_EQ("id2122", breakLocation->GetScriptId()); + EXPECT_EQ(breakLocation->GetLine(), 8299); +} + +HWTEST_F_L0(DebuggerTypesTest, BreakLocationToObjectTest) +{ + CString msg; + std::unique_ptr breakLocation; + Local tmpStr; + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "scriptId":"id12","lineNumber":919,"columnNumber":148,"type":"call" + }})"; + breakLocation = BreakLocation::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(breakLocation, nullptr); + Local object = breakLocation->ToObject(ecmaVm); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "scriptId"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + Local result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("id12", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "lineNumber"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 919); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "columnNumber"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ(Local(result)->Value(), 148); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "type"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("call", Local(result)->ToString()); +} + +HWTEST_F_L0(DebuggerTypesTest, ScopeCreateTest) +{ + CString msg; + std::unique_ptr scope; + + // abnormal params of null msg + msg = CString() + R"({})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of unexist key params + msg = CString() + R"({"id":0,"method":"Debugger.Test"})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of null params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of unknown params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"ss","object":{..}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"ss","object":{"type":")" + + ObjectType::Bigint + R"("}}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":12,"object":{..}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":12,"object":{"type":")" + + ObjectType::Bigint + R"("}}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":10}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":{..}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"ww":")" + + ObjectType::Bigint + R"("}}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":{..},"name":10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"name":10}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":{..},"name":["10"]] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"name":["10"]}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // normal params of params.sub-key=["type":"global","object":{..},"name":"name128"] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"name":"name117"}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(scope, nullptr); + EXPECT_EQ("name117", scope->GetName()); + + // abnormal params of params.sub-key=["type":"global","object":{..},"startLocation":10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"startLocation":10}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":{..},"startLocation":{"12":"34"}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"startLocation":{"12":"34"}}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":{..},"endLocation":10] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"endLocation":10}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // abnormal params of params.sub-key=["type":"global","object":{..},"endLocation":{"12":"34"}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("},"endLocation":{"12":"34"}}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(scope, nullptr); + + // normal params of params.sub-key=["type":"global","object":{..}] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Bigint + R"("}}})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(scope, nullptr); + EXPECT_EQ("global", scope->GetType()); + RemoteObject *object = scope->GetObject(); + ASSERT_NE(object, nullptr); + EXPECT_EQ(object->GetType(), ObjectType::Bigint); +} + +HWTEST_F_L0(DebuggerTypesTest, ScopeToObjectTest) +{ + CString msg; + std::unique_ptr scope; + Local tmpStr; + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "type":"global","object":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Dataview + R"("},"name":"name9", + "startLocation":{"scriptId":"id2","lineNumber":99}, + "endLocation":{"scriptId":"id13","lineNumber":146} + }})"; + scope = Scope::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(scope, nullptr); + Local object = scope->ToObject(ecmaVm); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "type"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + Local result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("global", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "object"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "type"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + Local subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "subtype"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectSubType::Dataview.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "name"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("name9", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "startLocation"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "scriptId"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ("id2", Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "lineNumber"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(Local(subResult)->Value(), 99); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "endLocation"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "scriptId"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ("id13", Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "lineNumber"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(Local(subResult)->Value(), 146); +} + +HWTEST_F_L0(DebuggerTypesTest, CallFrameCreateTest) +{ + CString msg; + std::unique_ptr callFrame; + + // abnormal params of null msg + msg = CString() + R"({})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of unexist key params + msg = CString() + R"({"id":0,"method":"Debugger.Test"})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of null params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of unknown params.sub-key + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{"unknownKey":100}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":10,"functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":["id0"],"functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":10, + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":["name0"], + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0","functionLocation":10, + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0","functionLocation":{"scriptId11":"id5","lineNumber":19}, + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0", + "location":{"scriptId2":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0", + "location":10,"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":10,"scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":{"url":"url7"},"scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0", "location":{"scriptId":"id5","lineNumber":19}, + "url":"url7","scopeChain":10,"this":{"type":")" + + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + {"type":"22","object":{"type":")" + + ObjectType::Object + R"("}},"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":10}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"11":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}, + "returnValue":10}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // abnormal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}, + "returnValue":{"type":"object","subtype":"11"}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + EXPECT_EQ(callFrame, nullptr); + + // normal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0", + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(callFrame, nullptr); + EXPECT_EQ("id0", callFrame->GetCallFrameId()); + EXPECT_EQ("name0", callFrame->GetFunctionName()); + ASSERT_FALSE(callFrame->HasFunctionLocation()); + Location *location = callFrame->GetLocation(); + EXPECT_EQ("id5", location->GetScriptId()); + EXPECT_EQ(location->GetLine(), 19); + EXPECT_EQ("url7", callFrame->GetUrl()); + const CVector> *scopeChain = callFrame->GetScopeChain(); + EXPECT_EQ(scopeChain->size(), 2); + RemoteObject *thisObj = callFrame->GetThis(); + ASSERT_NE(thisObj, nullptr); + EXPECT_EQ(thisObj->GetType(), ObjectType::Object); + EXPECT_EQ(thisObj->GetSubType(), ObjectSubType::V128); + ASSERT_FALSE(callFrame->HasReturnValue()); + + // normal params of params.sub-key=[..] + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0","functionLocation":{"scriptId":"id3","lineNumber":16}, + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::V128 + + R"("},"returnValue":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::I32 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(callFrame, nullptr); + EXPECT_EQ("id0", callFrame->GetCallFrameId()); + EXPECT_EQ("name0", callFrame->GetFunctionName()); + Location *functionLocation = callFrame->GetFunctionLocation(); + EXPECT_EQ("id3", functionLocation->GetScriptId()); + EXPECT_EQ(functionLocation->GetLine(), 16); + location = callFrame->GetLocation(); + EXPECT_EQ("id5", location->GetScriptId()); + EXPECT_EQ(location->GetLine(), 19); + EXPECT_EQ("url7", callFrame->GetUrl()); + scopeChain = callFrame->GetScopeChain(); + EXPECT_EQ(scopeChain->size(), 2); + thisObj = callFrame->GetThis(); + ASSERT_NE(thisObj, nullptr); + EXPECT_EQ(thisObj->GetType(), ObjectType::Object); + EXPECT_EQ(thisObj->GetSubType(), ObjectSubType::V128); + RemoteObject *returnObj = callFrame->GetReturnValue(); + ASSERT_NE(returnObj, nullptr); + EXPECT_EQ(returnObj->GetType(), ObjectType::Object); + EXPECT_EQ(returnObj->GetSubType(), ObjectSubType::I32); +} + +HWTEST_F_L0(DebuggerTypesTest, CallFrameToObjectTest) +{ + CString msg; + std::unique_ptr callFrame; + Local tmpStr; + + msg = CString() + R"({"id":0,"method":"Debugger.Test","params":{ + "callFrameId":"id0","functionName":"name0","functionLocation":{"scriptId":"id3","lineNumber":16}, + "location":{"scriptId":"id5","lineNumber":19},"url":"url7","scopeChain": + [{"type":"global","object":{"type":")" + + ObjectType::Object + R"("}}, {"type":"local","object":{"type":")" + ObjectType::Object + + R"("}}],"this":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::Iterator + + R"("},"returnValue":{"type":")" + ObjectType::Object + R"(","subtype":")" + ObjectSubType::I64 + R"("}}})"; + callFrame = CallFrame::Create(ecmaVm, DispatchRequest(ecmaVm, msg).GetParams()); + ASSERT_NE(callFrame, nullptr); + Local object = callFrame->ToObject(ecmaVm); + + tmpStr = StringRef::NewFromUtf8(ecmaVm, "callFrameId"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + Local result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("id0", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "functionName"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("name0", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "functionLocation"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "scriptId"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + Local subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ("id3", Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "lineNumber"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(Local(subResult)->Value(), 16); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "location"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "scriptId"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ("id5", Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "lineNumber"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(Local(subResult)->Value(), 19); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "url"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + EXPECT_EQ("url7", Local(result)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "scopeChain"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsArray(ecmaVm)); + + EXPECT_EQ(Local(result)->Length(ecmaVm), 2); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "this"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "type"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "subtype"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectSubType::Iterator.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "returnValue"); + ASSERT_TRUE(object->Has(ecmaVm, tmpStr)); + result = object->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!result.IsEmpty()); + ASSERT_TRUE(result->IsObject()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "type"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectType::Object.c_str()), Local(subResult)->ToString()); + tmpStr = StringRef::NewFromUtf8(ecmaVm, "subtype"); + ASSERT_TRUE(Local(result)->Has(ecmaVm, Local(tmpStr))); + subResult = Local(result)->Get(ecmaVm, tmpStr); + ASSERT_TRUE(!subResult.IsEmpty()); + EXPECT_EQ(std::string(ObjectSubType::I64.c_str()), Local(subResult)->ToString()); +} + +} // namespace panda::test diff --git a/ecmascript/tooling/test/init.cpp b/ecmascript/tooling/test/init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a1a0c155a0e15ecef2bae08c68e871dd0ae9285f --- /dev/null +++ b/ecmascript/tooling/test/init.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "ecmascript/tooling/test/test_runner.h" +#include "ecmascript/tooling/test/test_util.h" + +namespace panda::tooling::ecmascript::test { +extern const char *GetCurrentTestName(); + +static std::thread g_debuggerThread; + +static std::unique_ptr g_runner{nullptr}; + +extern "C" int32_t StartDebugger(EcmaVM *vm) +{ + const char *testName = GetCurrentTestName(); + g_runner = std::make_unique(testName, vm); + g_debuggerThread = std::thread([] { + TestUtil::WaitForInit(); + g_runner->Run(); + }); + return 0; +} + +extern "C" int32_t StopDebugger() +{ + g_debuggerThread.join(); + g_runner->TerminateTest(); + g_runner.reset(); + return 0; +} +} // namespace panda::tooling::ecmascript::test diff --git a/ecmascript/tooling/test/js/Sample.js b/ecmascript/tooling/test/js/Sample.js new file mode 100644 index 0000000000000000000000000000000000000000..d4796cf45303dd5dde2a9c33f244064f8c502f99 --- /dev/null +++ b/ecmascript/tooling/test/js/Sample.js @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function count_to_ten() { + var a = 1; + a = 2; + a = 3; + a = 4; + a = 5; + a = 6; + a = 7; + a = 8; + a = 9; + a = 10; +} + +count_to_ten(); +count_to_ten(); \ No newline at end of file diff --git a/ecmascript/tooling/test/launcher.cpp b/ecmascript/tooling/test/launcher.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ca5e968a96356999cdd759f47d750b2e8083b3e --- /dev/null +++ b/ecmascript/tooling/test/launcher.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/napi/include/jsnapi.h" +#include "ecmascript/tests/test_helper.h" +#include "ecmascript/tooling/test/test_list.h" + +namespace panda::tooling::ecmascript::test { +using panda::ecmascript::EcmaHandleScope; +using panda::ecmascript::EcmaVM; +using panda::ecmascript::JSThread; +using panda::test::TestHelper; + +class EcmaScriptDebugApiTest : public testing::TestWithParam { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + +protected: + void RunEcmaScriptTest(const char *testName) + { + std::cout << "Running " << testName << std::endl; + SetCurrentTestName(testName); + EcmaVM *vm = EcmaVM::Cast(instance); + ASSERT_NE(vm, nullptr); + auto [pandaFile, entryPoint] = GetTestEntryPoint(testName); + + auto fileNameRef = StringRef::NewFromUtf8(vm, pandaFile.c_str(), pandaFile.size()); + auto entryRef = StringRef::NewFromUtf8(vm, entryPoint.c_str(), entryPoint.size()); + auto res = JSNApi::Execute(vm, fileNameRef, entryRef); + ASSERT_TRUE(res); + } +}; + +HWTEST_P_L0(EcmaScriptDebugApiTest, EcmaScriptSuite) +{ + const char *testName = GetParam(); + ASSERT_NE(testName, nullptr); + RunEcmaScriptTest(testName); +} + +INSTANTIATE_TEST_CASE_P(EcmaDebugApiTest, EcmaScriptDebugApiTest, + testing::ValuesIn(GetTestList(panda::panda_file::SourceLang::ECMASCRIPT))); +} // namespace panda::tooling::ecmascript::test diff --git a/ecmascript/tooling/test/test_extractor.cpp b/ecmascript/tooling/test/test_extractor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..80767107cce52d20d0bb72f46174caa21f2eae5e --- /dev/null +++ b/ecmascript/tooling/test/test_extractor.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Taken from panda-tools/panda-debugger/debugger + +#include "ecmascript/tooling/test/test_extractor.h" + +#include +#include + +#include "libpandabase/utils/leb128.h" +#include "libpandabase/utils/utf.h" +#include "libpandafile/class_data_accessor-inl.h" +#include "libpandafile/code_data_accessor-inl.h" +#include "libpandafile/debug_data_accessor-inl.h" +#include "libpandafile/helpers.h" +#include "libpandafile/method_data_accessor-inl.h" +#include "libpandafile/proto_data_accessor-inl.h" + +namespace panda::tooling::ecmascript::test { +std::pair TestExtractor::GetBreakpointAddress(const SourceLocation &source_location) +{ + auto pos = source_location.path.find_last_of("/\\"); + auto name = source_location.path; + + if (pos != CString::npos) { + name = name.substr(pos + 1); + } + + std::vector methods = GetMethodIdList(); + for (const auto &method : methods) { + auto srcName = CString(GetSourceFile(method)); + auto pos_sf = srcName.find_last_of("/\\"); + if (pos_sf != CString::npos) { + srcName = srcName.substr(pos_sf + 1); + } + if (srcName == name) { + const panda_file::LineNumberTable &lineTable = GetLineNumberTable(method); + if (lineTable.empty()) { + continue; + } + + std::optional offset = GetOffsetByTableLineNumber(lineTable, source_location.line); + if (offset == std::nullopt) { + continue; + } + return {method, offset.value()}; + } + } + return {EntityId(), 0}; +} + +CList TestExtractor::GetStepRanges(EntityId method_id, uint32_t current_offset) +{ + const panda_file::LineNumberTable &lineTable = GetLineNumberTable(method_id); + if (lineTable.empty()) { + return {}; + } + + std::optional line = GetLineNumberByTableOffset(lineTable, current_offset); + if (line == std::nullopt) { + return {}; + } + + CList res; + for (auto it = lineTable.begin(); it != lineTable.end(); ++it) { + if (it->line == line) { + size_t idx = it - lineTable.begin(); + if (it + 1 != lineTable.end()) { + res.push_back({lineTable[idx].offset, lineTable[idx + 1].offset}); + } else { + res.push_back({lineTable[idx].offset, std::numeric_limits::max()}); + } + } + } + return res; +} + +std::vector TestExtractor::GetLocalVariableInfo(EntityId method_id, size_t offset) +{ + const std::vector &variables = GetLocalVariableTable(method_id); + std::vector result; + + for (const auto &variable : variables) { + if (variable.start_offset <= offset && offset <= variable.end_offset) { + result.push_back(variable); + } + } + return result; +} + +std::optional TestExtractor::GetLineNumberByTableOffset(const panda_file::LineNumberTable &table, + uint32_t offset) +{ + for (const auto &value : table) { + if (value.offset == offset) { + return value.line; + } + } + return std::nullopt; +} + +std::optional TestExtractor::GetOffsetByTableLineNumber(const panda_file::LineNumberTable &table, size_t line) +{ + for (const auto &value : table) { + if (value.line == line) { + return value.offset; + } + } + return std::nullopt; +} + +SourceLocation TestExtractor::GetSourceLocation(EntityId method_id, uint32_t bytecode_offset) +{ + const panda_file::LineNumberTable &lineTable = GetLineNumberTable(method_id); + if (lineTable.empty()) { + return SourceLocation(); + } + + std::optional line = GetLineNumberByTableOffset(lineTable, bytecode_offset); + if (line == std::nullopt) { + return SourceLocation(); + } + + return SourceLocation{GetSourceFile(method_id), line.value()}; +} +} // namespace panda::tooling::ecmascript::test diff --git a/ecmascript/tooling/test/test_extractor.h b/ecmascript/tooling/test/test_extractor.h new file mode 100644 index 0000000000000000000000000000000000000000..d1f9b713439590b01a3f73d32d41a493c5b84e46 --- /dev/null +++ b/ecmascript/tooling/test/test_extractor.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Taken from panda-tools/panda-debugger/debugger +#ifndef PANDA_RUNTIME_DEBUG_TEST_DEBUG_INFO_EXTRACTOR_H +#define PANDA_RUNTIME_DEBUG_TEST_DEBUG_INFO_EXTRACTOR_H + +#include "ecmascript/mem/c_string.h" +#include "ecmascript/tooling/pt_js_extractor.h" + +namespace panda::tooling::ecmascript::test { +using EntityId = panda_file::File::EntityId; +using panda::ecmascript::CString; +using panda::tooling::ecmascript::PtJSExtractor; + +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +struct SourceLocation { + CString path; // NOLINT(misc-non-private-member-variables-in-classes) + size_t line; // NOLINT(misc-non-private-member-variables-in-classes) + + bool operator==(const SourceLocation &other) const + { + return path == other.path && line == other.line; + } + + bool IsValid() const + { + return !path.empty(); + } +}; + +class TestExtractor : public PtJSExtractor { +public: + explicit TestExtractor(const panda_file::File *pandaFileData) : PtJSExtractor(pandaFileData) {} + ~TestExtractor() = default; + + std::pair GetBreakpointAddress(const SourceLocation &sourceLocation); + + CList GetStepRanges(EntityId methodId, uint32_t currentOffset); + + std::vector GetLocalVariableInfo(EntityId methodId, size_t offset); + + SourceLocation GetSourceLocation(EntityId methodId, uint32_t bytecodeOffset); + + std::optional GetLineNumberByTableOffset(const panda_file::LineNumberTable &table, uint32_t offset); + std::optional GetOffsetByTableLineNumber(const panda_file::LineNumberTable &table, size_t line); +}; +} // namespace panda::tooling::ecmascript::test + +#endif // PANDA_RUNTIME_DEBUG_TEST_DEBUG_INFO_EXTRACTOR_H diff --git a/ecmascript/tooling/test/test_list.cpp b/ecmascript/tooling/test/test_list.cpp new file mode 100644 index 0000000000000000000000000000000000000000..51ebe3da53a5fa5085cfff13b5759e201746d414 --- /dev/null +++ b/ecmascript/tooling/test/test_list.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/test/test_list.h" +#include "ecmascript/tooling/test/api_tests/api_tests.h" +#include "ecmascript/tooling/test/test_util.h" + +namespace panda::tooling::ecmascript::test { +static const char *g_currentTestName = nullptr; + +static void RegisterTests() +{ + TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsBreakpoint", GetJsBreakpointTest()); + TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsSingleStepTest", GetJsSingleStepTest()); +} + +std::vector GetTestList(panda_file::SourceLang language) +{ + RegisterTests(); + std::vector res; + auto &tests = TestUtil::GetTests(); + auto languageIt = tests.find(language); + if (languageIt == tests.end()) { + return {}; + } + + for (const auto &entry : languageIt->second) { + res.push_back(entry.first); + } + return res; +} + +void SetCurrentTestName(const char *testName) +{ + g_currentTestName = testName; +} + +const char *GetCurrentTestName() +{ + return g_currentTestName; +} + +std::pair GetTestEntryPoint(const char *testName) +{ + return TestUtil::GetTest(testName)->GetEntryPoint(); +} +} // namespace panda::tooling::ecmascript::test diff --git a/ecmascript/tooling/test/test_list.h b/ecmascript/tooling/test/test_list.h new file mode 100644 index 0000000000000000000000000000000000000000..93500a4fef8afbff7f36d9e20364f8146da942b0 --- /dev/null +++ b/ecmascript/tooling/test/test_list.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_DEBUG_TEST_TEST_LIST_H +#define PANDA_RUNTIME_DEBUG_TEST_TEST_LIST_H + +#include +#include + +#include "ecmascript/mem/c_string.h" +#include "libpandafile/file_items.h" + +namespace panda::tooling::ecmascript::test { +using panda::ecmascript::CString; + +std::vector GetTestList(panda_file::SourceLang language); + +void SetCurrentTestName(const char *testName); + +std::pair GetTestEntryPoint(const char *testName); +} // namespace panda::tooling::ecmascript::test + +#endif // PANDA_RUNTIME_DEBUG_TEST_TEST_LIST_H diff --git a/ecmascript/tooling/test/test_runner.h b/ecmascript/tooling/test/test_runner.h new file mode 100644 index 0000000000000000000000000000000000000000..11ed3577b81c01e69a7070afe689a03558fa684d --- /dev/null +++ b/ecmascript/tooling/test/test_runner.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_DEBUG_TEST_TEST_RUNNER_H +#define PANDA_RUNTIME_DEBUG_TEST_TEST_RUNNER_H + +#include "ecmascript/tooling/test/test_util.h" +#include "ecmascript/tooling/agent/js_pt_hooks.h" +#include "ecmascript/tooling/agent/js_backend.h" + +namespace panda::tooling::ecmascript::test { +class TestRunner : public PtHooks { +public: + TestRunner(const char *test_name, EcmaVM *vm) + { + backend_ = std::make_unique(vm); + test_name_ = test_name; + test_ = TestUtil::GetTest(test_name); + test_->backend = backend_.get(); + test_->debug_interface = backend_->GetDebugger(); + debug_interface_ = backend_->GetDebugger(); + TestUtil::Reset(); + debug_interface_->RegisterHooks(this); + } + + void Run() + { + if (test_->scenario) { + test_->scenario(); + } + } + + void Breakpoint(PtThread ecmaVm, const PtLocation &location) override + { + if (test_->breakpoint) { + test_->breakpoint(ecmaVm, location); + } + } + + void LoadModule(std::string_view panda_file_name) override + { + if (test_->load_module) { + test_->load_module(panda_file_name); + } + } + + void Paused(PauseReason reason) override + { + if (test_->paused) { + test_->paused(reason); + } + }; + + void Exception(PtThread ecmaVm, const PtLocation &location, [[maybe_unused]] PtObject exceptionObject, + [[maybe_unused]] const PtLocation &catchLocation) override + { + if (test_->exception) { + test_->exception(ecmaVm, location); + } + } + + void MethodEntry(PtThread ecmaVm, PtMethod method) override + { + if (test_->method_entry) { + test_->method_entry(ecmaVm, method); + } + } + + void SingleStep(PtThread ecmaVm, const PtLocation &location) override + { + if (test_->single_step) { + test_->single_step(ecmaVm, location); + } + } + + void VmDeath() override + { + if (test_->vm_death) { + test_->vm_death(); + } + TestUtil::Event(DebugEvent::VM_DEATH); + } + + void VmInitialization([[maybe_unused]] PtThread ecmaVm) override + { + if (test_->vm_init) { + test_->vm_init(); + } + TestUtil::Event(DebugEvent::VM_INITIALIZATION); + } + + void VmStart() override + { + if (test_->vm_start) { + test_->vm_start(); + } + } + + void TerminateTest() + { + debug_interface_->RegisterHooks(nullptr); + if (TestUtil::IsTestFinished()) { + return; + } + LOG(FATAL, DEBUGGER) << "Test " << test_name_ << " failed"; + } + + void ThreadStart(PtThread) override {} + void ThreadEnd(PtThread) override {} + void ClassLoad(PtThread, PtClass) override {} + void ClassPrepare(PtThread, PtClass) override {} + void MonitorWait(PtThread, PtObject, int64_t) override {} + void MonitorWaited(PtThread, PtObject, bool) override {} + void MonitorContendedEnter(PtThread, PtObject) override {} + void MonitorContendedEntered(PtThread, PtObject) override {} + void ExceptionCatch(PtThread, const PtLocation &, PtObject) override {} + void PropertyAccess(PtThread, const PtLocation &, PtObject, PtProperty) override {} + void PropertyModification(PtThread, const PtLocation &, PtObject, PtProperty, PtValue) override {} + void FramePop(PtThread, PtMethod, bool) override {} + void GarbageCollectionFinish() override {} + void GarbageCollectionStart() override {} + void ObjectAlloc(PtClass, PtObject, PtThread, size_t) override {} + void MethodExit(PtThread, PtMethod, bool, PtValue) override {} + void ExceptionRevoked(ExceptionWrapper, ExceptionID) override {} + void ExecutionContextCreated(ExecutionContextWrapper) override {} + void ExecutionContextDestroyed(ExecutionContextWrapper) override {} + void ExecutionContextsCleared() override {} + void InspectRequested(PtObject, PtObject) override {} + + ~TestRunner() = default; + +private: + std::unique_ptr backend_ {nullptr}; + JSDebugger *debug_interface_; + const char *test_name_; + ApiTest *test_; +}; +} // namespace panda::tooling::ecmascript::test + +#endif // PANDA_RUNTIME_DEBUG_TEST_TEST_RUNNER_H diff --git a/ecmascript/tooling/test/test_util.cpp b/ecmascript/tooling/test/test_util.cpp new file mode 100644 index 0000000000000000000000000000000000000000..130a267411e1970759004610227a54440f801455 --- /dev/null +++ b/ecmascript/tooling/test/test_util.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/tooling/test/test_util.h" + +namespace panda::tooling::ecmascript::test { +TestMap TestUtil::testMap_; +os::memory::Mutex TestUtil::eventMutex_; +os::memory::ConditionVariable TestUtil::eventCv_; +DebugEvent TestUtil::lastEvent_ = DebugEvent::UNINITIALIZED; +bool TestUtil::initialized_ = false; +os::memory::Mutex TestUtil::suspendMutex_; +os::memory::ConditionVariable TestUtil::suspendCv_; +bool TestUtil::suspended_; +PtThread TestUtil::lastEventThread_ = PtThread::NONE; +PtLocation TestUtil::lastEventLocation_("", EntityId(0), 0); + +std::vector TestUtil::GetVariables(JSMethod *method, uint32_t offset) +{ + auto methodId = method->GetFileId(); + auto pandaFile = method->GetPandaFile()->GetFilename().c_str(); + PtLocation location(pandaFile, methodId, offset); + return GetVariables(location); +} + +std::vector TestUtil::GetVariables(PtLocation location) +{ + std::unique_ptr uFile = panda_file::File::Open(location.GetPandaFile()); + const panda_file::File *pf = uFile.get(); + if (pf == nullptr) { + return {}; + } + + TestExtractor extractor(pf); + return extractor.GetLocalVariableInfo(location.GetMethodId(), location.GetBytecodeOffset()); +} + +int32_t TestUtil::GetValueRegister(JSMethod *method, const char *varName) +{ + auto variables = TestUtil::GetVariables(method, 0); + for (const auto &var : variables) { + if (var.name == varName) { + return var.reg_number; + } + } + + return -2; +} + +std::ostream &operator<<(std::ostream &out, DebugEvent value) +{ + const char *s = nullptr; + +#define ADD_CASE(entry) \ + case (entry): \ + s = #entry; \ + break; + + switch (value) { + ADD_CASE(DebugEvent::BREAKPOINT); + ADD_CASE(DebugEvent::LOAD_MODULE); + ADD_CASE(DebugEvent::PAUSED); + ADD_CASE(DebugEvent::EXCEPTION); + ADD_CASE(DebugEvent::METHOD_ENTRY); + ADD_CASE(DebugEvent::SINGLE_STEP); + ADD_CASE(DebugEvent::VM_START); + ADD_CASE(DebugEvent::VM_INITIALIZATION); + ADD_CASE(DebugEvent::VM_DEATH); + default: { + ASSERT(false && "Unknown DebugEvent"); // NOLINT + } + } + +#undef ADD_CASE + + return out << s; +} + +std::ostream &operator<<(std::ostream &out, std::nullptr_t) +{ + return out << "nullptr"; +} + +ApiTest::ApiTest() +{ + scenario = []() { + ASSERT_EXITED(); + return true; + }; +} +} // namespace panda::tooling::ecmascript::test diff --git a/ecmascript/tooling/test/test_util.h b/ecmascript/tooling/test/test_util.h new file mode 100644 index 0000000000000000000000000000000000000000..55eebc5cd58d974527b27522f13f1a4ae7ec912b --- /dev/null +++ b/ecmascript/tooling/test/test_util.h @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_DEBUG_TEST_TEST_UTIL_H +#define PANDA_RUNTIME_DEBUG_TEST_TEST_UTIL_H + +#include +#include +#include +#include +#include + +#include "api_test.h" +#include "ecmascript/tooling/interface/js_debugger.h" +#include "os/mutex.h" +#include "test_extractor.h" + +namespace panda::tooling::ecmascript::test { +using TestMap = std::unordered_map>>; + +class TestUtil { +public: + static void RegisterTest(panda_file::SourceLang language, const char *testName, std::unique_ptr test) + { + auto it = testMap_.find(language); + if (it == testMap_.end()) { + std::unordered_map> entry; + auto res = testMap_.emplace(language, std::move(entry)); + it = res.first; + } + it->second.insert({testName, std::move(test)}); + } + + static ApiTest *GetTest(const char *name) + { + for (auto it = testMap_.begin(); it != testMap_.end(); ++it) { + auto &internalMap = it->second; + auto internalIt = std::find_if(internalMap.begin(), internalMap.end(), + [name](auto &it) { return !::strcmp(it.first, name); }); + if (internalIt != internalMap.end()) { + return internalIt->second.get(); + } + } + LOG(FATAL, DEBUGGER) << "Test " << name << " not found"; + return nullptr; + } + + static PtThread WaitForBreakpoint(PtLocation location) + { + PtThread stoppedThread(PtThread::NONE); + auto predicate = [&location]() REQUIRES(eventMutex_) { return lastEventLocation_ == location; }; + auto onSuccess = [&stoppedThread]() REQUIRES(eventMutex_) { + stoppedThread = lastEventThread_; + + // Need to reset location, because we might want to stop at the same point + lastEventLocation_ = PtLocation("", EntityId(0), 0); + }; + + WaitForEvent(DebugEvent::BREAKPOINT, predicate, onSuccess); + return stoppedThread; + } + + static bool WaitForExit() + { + return WaitForEvent( + DebugEvent::VM_DEATH, []() REQUIRES(eventMutex_) { return lastEvent_ == DebugEvent::VM_DEATH; }, [] {}); + } + + static bool WaitForInit() + { + return WaitForEvent( + DebugEvent::VM_INITIALIZATION, []() REQUIRES(eventMutex_) { return initialized_; }, [] {}); + } + + static void Event(DebugEvent event, PtThread ecmaVm = PtThread::NONE, + PtLocation location = PtLocation("", EntityId(0), 0)) + { + LOG(DEBUG, DEBUGGER) << "Occured event " << event << " in ecmaVm with id " << ecmaVm.GetId(); + os::memory::LockHolder holder(eventMutex_); + lastEvent_ = event; + lastEventThread_ = ecmaVm; + lastEventLocation_ = location; + if (event == DebugEvent::VM_INITIALIZATION) { + initialized_ = true; + } + eventCv_.Signal(); + } + + static void Reset() + { + os::memory::LockHolder lock(eventMutex_); + initialized_ = false; + lastEvent_ = DebugEvent::VM_START; + } + + static TestMap &GetTests() + { + return testMap_; + } + + static bool IsTestFinished() + { + os::memory::LockHolder lock(eventMutex_); + return lastEvent_ == DebugEvent::VM_DEATH; + } + + static PtLocation GetLocation(const char *sourceFile, uint32_t line, const char *pandaFile) + { + std::unique_ptr uFile = panda_file::File::Open(pandaFile); + const panda_file::File *pf = uFile.get(); + if (pf == nullptr) { + return PtLocation("", EntityId(0), 0); + } + + TestExtractor extractor(pf); + auto [id, offset] = extractor.GetBreakpointAddress({sourceFile, line}); +#if PANDA_LIBCORE_SUPPORT + static std::unordered_set absolute_paths; + static char buf[PATH_MAX]; + auto res = absolute_paths.insert(::realpath(pandaFile, buf)); + return PtLocation(res.first->c_str(), id, offset); +#else // PANDA_LIBCORE_SUPPORT + return PtLocation(pandaFile, id, offset); +#endif // PANDA_LIBCORE_SUPPORT + } + + static std::vector GetVariables(JSMethod *method, uint32_t offset); + + static std::vector GetVariables(PtLocation location); + + static int32_t GetValueRegister(JSMethod *method, const char *varName); + + static bool SuspendUntilContinue(DebugEvent reason, PtThread ecmaVm, PtLocation location) + { + { + os::memory::LockHolder lock(suspendMutex_); + suspended_ = true; + } + + // Notify the debugger ecmaVm about the suspend event + Event(reason, ecmaVm, location); + + // Wait for continue + { + os::memory::LockHolder lock(suspendMutex_); + while (suspended_) { + suspendCv_.Wait(&suspendMutex_); + } + } + + return true; + } + + static bool Continue() + { + os::memory::LockHolder lock(suspendMutex_); + suspended_ = false; + suspendCv_.Signal(); + return true; + } + +private: + template + static bool WaitForEvent(DebugEvent event, Predicate predicate, OnSuccessAction action) + { + os::memory::LockHolder holder(eventMutex_); + while (!predicate()) { + if (lastEvent_ == DebugEvent::VM_DEATH) { + return false; + } + constexpr uint64_t TIMEOUT_MSEC = 100000U; + bool timeExceeded = eventCv_.TimedWait(&eventMutex_, TIMEOUT_MSEC); + if (timeExceeded) { + LOG(FATAL, DEBUGGER) << "Time limit exceeded while waiting " << event; + return false; + } + } + action(); + return true; + } + + static TestMap testMap_; + static os::memory::Mutex eventMutex_; + static os::memory::ConditionVariable eventCv_ GUARDED_BY(eventMutex_); + static DebugEvent lastEvent_ GUARDED_BY(eventMutex_); + static PtThread lastEventThread_ GUARDED_BY(eventMutex_); + static PtLocation lastEventLocation_ GUARDED_BY(eventMutex_); + static os::memory::Mutex suspendMutex_; + static os::memory::ConditionVariable suspendCv_ GUARDED_BY(suspendMutex_); + static bool suspended_ GUARDED_BY(suspendMutex_); + static bool initialized_ GUARDED_BY(eventMutex_); +}; + +std::ostream &operator<<(std::ostream &out, std::nullptr_t); + +#define _ASSERT_FAIL(val1, val2, strval1, strval2, msg) \ + std::cerr << "Assertion failed at " << __FILE__ << ':' << __LINE__ << std::endl; \ + std::cerr << "Expected that " strval1 " is " << msg << " " strval2 << std::endl; \ + std::cerr << "\t" strval1 ": " << (val1) << std::endl; \ + std::cerr << "\t" strval2 ": " << (val2) << std::endl; \ + std::abort(); + +#define ASSERT_TRUE(cond) \ + do { \ + auto res = (cond); \ + if (!res) { \ + _ASSERT_FAIL(res, true, #cond, "true", "equal to") \ + } \ + } while (0) + +#define ASSERT_FALSE(cond) \ + do { \ + auto res = (cond); \ + if (res) { \ + _ASSERT_FAIL(res, false, #cond, "false", "equal to") \ + } \ + } while (0) + +#define ASSERT_EQ(lhs, rhs) \ + do { \ + auto res1 = (lhs); \ + decltype(res1) res2 = (rhs); \ + if (res1 != res2) { \ + _ASSERT_FAIL(res1, res2, #lhs, #rhs, "equal to") \ + } \ + } while (0) + +#define ASSERT_NE(lhs, rhs) \ + do { \ + auto res1 = (lhs); \ + decltype(res1) res2 = (rhs); \ + if (res1 == res2) { \ + _ASSERT_FAIL(res1, res2, #lhs, #rhs, "not equal to") \ + } \ + } while (0) + +#define ASSERT_STREQ(lhs, rhs) \ + do { \ + auto res1 = (lhs); \ + decltype(res1) res2 = (rhs); \ + if (::strcmp(res1, res2) != 0) { \ + _ASSERT_FAIL(res1, res2, #lhs, #rhs, "equal to") \ + } \ + } while (0) + +#define ASSERT_SUCCESS(api_call) \ + do { \ + auto error = api_call; \ + if (error) { \ + _ASSERT_FAIL(error.value().GetMessage(), "Success", "API call result", "Expected", "") \ + } \ + } while (0) + +#define ASSERT_EXITED() \ + do { \ + bool res = TestUtil::WaitForExit(); \ + if (!res) { \ + _ASSERT_FAIL(TestUtil::IsTestFinished(), true, "TestUtil::IsTestFinished()", "true", "") \ + } \ + } while (0) + +#define ASSERT_LOCATION_EQ(lhs, rhs) \ + do { \ + ASSERT_STREQ(lhs.GetPandaFile(), rhs.GetPandaFile()); \ + ASSERT_EQ(lhs.GetMethodId().GetOffset(), rhs.GetMethodId().GetOffset()); \ + ASSERT_EQ(lhs.GetBytecodeOffset(), rhs.GetBytecodeOffset()); \ + } while (0); + +#define ASSERT_THREAD_VALID(ecmaVm) \ + do { \ + ASSERT_NE(ecmaVm.GetId(), PtThread::NONE.GetId()); \ + } while (0); + +#define ASSERT_BREAKPOINT_SUCCESS(location) \ + do { \ + PtThread suspended = TestUtil::WaitForBreakpoint(location); \ + ASSERT_THREAD_VALID(suspended); \ + } while (0); +} // namespace panda::tooling::ecmascript::test +#endif // PANDA_RUNTIME_DEBUG_TEST_TEST_UTIL_H diff --git a/ecmascript/transitions_dictionary.h b/ecmascript/transitions_dictionary.h new file mode 100644 index 0000000000000000000000000000000000000000..de114ab494317192529319969060187bd947e1bb --- /dev/null +++ b/ecmascript/transitions_dictionary.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_TRANSITIONS_DICTIONARY_H +#define PANDA_RUNTIME_ECMASCRIPT_TRANSITIONS_DICTIONARY_H + +#include "ecmascript/tagged_hash_table-inl.h" +#include "ecmascript/js_symbol.h" +#include "ecmascript/accessor_data.h" +#include "ecmascript/js_tagged_number.h" + +namespace panda::ecmascript { +class TransitionsDictionary : public TaggedHashTable { +public: + using HashTableT = TaggedHashTable; + static inline bool IsMatch([[maybe_unused]] const JSTaggedValue &key, + [[maybe_unused]] const JSTaggedValue &otherKey) + { + UNREACHABLE(); + } + static inline int Hash([[maybe_unused]] const JSTaggedValue &key) + { + UNREACHABLE(); + } + + static inline bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &metaData, const JSTaggedValue &otherKey, + const JSTaggedValue &otherDetails) + { + return key == otherKey && metaData == otherDetails; + } + + static inline int Hash(const JSTaggedValue &key, const JSTaggedValue &metaData) + { + ASSERT(key.IsStringOrSymbol()); + + int hash = 0; + if (key.IsString()) { + hash = EcmaString::Cast(key.GetTaggedObject())->GetHashcode(); + } else if (key.IsSymbol()) { + hash = static_cast(JSSymbol::Cast(key.GetTaggedObject())->GetHashField()).GetInt(); + } + int metaDataHash = metaData.IsInt() ? metaData.GetInt() : static_cast(metaData.GetRawData()); + return hash + metaDataHash; + } + + inline static int GetKeyIndex(int entry) + { + return HashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return HashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return HashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + + static TransitionsDictionary *Cast(TaggedObject *object) + { + return reinterpret_cast(object); + } + + static constexpr int DEFAULT_ELEMENTS_NUMBER = 16; + static TransitionsDictionary *Create(const JSThread *thread, int numberOfElements = DEFAULT_ELEMENTS_NUMBER) + { + return HashTableT::Create(thread, numberOfElements); + } + + // Attempt to shrink the dictionary after deletion of key. + inline static TransitionsDictionary *Shrink(const JSThread *thread, + const JSHandle &dictionary) + { + return HashTableT::Shrink(thread, dictionary, 0); + } + + inline JSTaggedValue GetAttributes(int entry) const + { + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + return HashTableT::Get(index); + } + inline void SetAttributes(const JSThread *thread, int entry, JSTaggedValue metaData) + { + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + HashTableT::Set(thread, index, metaData); + } + + inline void SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, + const JSTaggedValue &metaData) + { + SetKey(thread, entry, key); + SetValue(thread, entry, value); + SetAttributes(thread, entry, metaData); + } + + inline void RemoveElement(const JSThread *thread, int entry) + { + SetKey(thread, entry, JSTaggedValue::Hole()); + SetValue(thread, entry, JSTaggedValue::Hole()); + SetAttributes(thread, entry, JSTaggedValue::Hole()); + IncreaseHoleEntriesCount(thread); + } + + int FindEntry(const JSTaggedValue &key, const JSTaggedValue &metaData); + static TransitionsDictionary *PutIfAbsent(const JSThread *thread, const JSHandle &dictionary, + const JSHandle &key, const JSHandle &value, + const JSHandle &metaData); + static TransitionsDictionary *Remove(const JSThread *thread, const JSHandle &table, + const JSHandle &key, const JSTaggedValue &metaData); + void Rehash(const JSThread *thread, TransitionsDictionary *newTable); + + static constexpr int ENTRY_SIZE = 3; + static constexpr int ENTRY_KEY_INDEX = 0; + static constexpr int ENTRY_VALUE_INDEX = 1; + static constexpr int ENTRY_DETAILS_INDEX = 2; + DECL_DUMP() +}; +} // namespace panda::ecmascript +#endif \ No newline at end of file diff --git a/ecmascript/vmstat/caller_stat.cpp b/ecmascript/vmstat/caller_stat.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45b97202ea63d8c4d26ed207fe71dbcfa52bd766 --- /dev/null +++ b/ecmascript/vmstat/caller_stat.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "caller_stat.h" + +namespace panda::ecmascript { +void PandaRuntimeTimer::Start(PandaRuntimeCallerStat *callerStat, PandaRuntimeTimer *parent) +{ + parent_ = parent; + callerStat_ = callerStat; + uint64_t nowTime = Now(); + if (parent != nullptr) { + parent_->Pause(nowTime); + } + Resume(nowTime); +} + +PandaRuntimeTimer *PandaRuntimeTimer::Stop() +{ + uint64_t nowTime = Now(); + Pause(nowTime); + if (parent_ != nullptr) { + parent_->Resume(nowTime); + } + UpdateCallerState(); + elapsed_ = 0; + return parent_; +} + +void PandaRuntimeTimer::Pause(uint64_t now) +{ + if (!IsStarted()) { + return; + } + elapsed_ += (now - start_); + start_ = 0; +} + +void PandaRuntimeTimer::Resume(uint64_t now) +{ + if (IsStarted()) { + return; + } + start_ = now; +} + +void PandaRuntimeTimer::Snapshot() +{ + uint64_t nowTime = Now(); + Pause(nowTime); + + PandaRuntimeTimer *timer = this; + while (timer != nullptr) { + timer->UpdateCallerState(); + timer = timer->parent_; + } + Resume(nowTime); +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/vmstat/caller_stat.h b/ecmascript/vmstat/caller_stat.h new file mode 100644 index 0000000000000000000000000000000000000000..f2c406c905bbe939c6e96f9b54206c438d2d1b3b --- /dev/null +++ b/ecmascript/vmstat/caller_stat.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_CALLER_STAT_H +#define PANDA_RUNTIME_CALLER_STAT_H + +#include +#include +#include // NOLINTNEXTLINE(modernize-deprecated-headers) + +#include "ecmascript/mem/c_string.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +class EcmaRuntimeStat; +class PandaRuntimeCallerStat { +public: + // NOLINTNEXTLINE(modernize-pass-by-value) + explicit PandaRuntimeCallerStat(const CString &name) : name_(name) {} + PandaRuntimeCallerStat() = default; + virtual ~PandaRuntimeCallerStat() = default; + + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(PandaRuntimeCallerStat); + DEFAULT_COPY_SEMANTIC(PandaRuntimeCallerStat); + + void UpdateState(uint64_t elapsed) + { + totalCount_++; + totalTime_ += elapsed; + maxTime_ = elapsed < maxTime_ ? maxTime_ : elapsed; + } + const char *Name() const + { + return name_.c_str(); + } + uint64_t TotalCount() const + { + return totalCount_; + } + uint64_t TotalTime() const + { + return totalTime_; + } + uint64_t MaxTime() const + { + return maxTime_; + } + + void Reset() + { + totalCount_ = 0; + totalTime_ = 0; + maxTime_ = 0; + } + +private: + CString name_{}; + uint64_t totalCount_{0}; + uint64_t totalTime_{0}; + uint64_t maxTime_{0}; +}; + +class PandaRuntimeTimer { +public: + void Start(PandaRuntimeCallerStat *callerStat, PandaRuntimeTimer *parent); + inline static uint64_t Now() + { + struct timespec timeNow = {0, 0}; + clock_gettime(CLOCK_REALTIME, &timeNow); + return timeNow.tv_sec * NANOSECONDSINSECOND + timeNow.tv_nsec; + } + + uint64_t Elapsed() const + { + return elapsed_; + } + + inline bool IsStarted() const + { + return start_ != 0; + } + + inline void SetParent(PandaRuntimeTimer *parent) + { + parent_ = parent; + } + + void Snapshot(); + + inline void UpdateCallerState() + { + callerStat_->UpdateState(elapsed_); + } + +private: + static constexpr uint64_t NANOSECONDSINSECOND = 1000000000; + PandaRuntimeTimer *Stop(); + void Pause(uint64_t now); + void Resume(uint64_t now); + PandaRuntimeCallerStat *callerStat_{nullptr}; + PandaRuntimeTimer *parent_{nullptr}; + uint64_t start_{0}; + uint64_t elapsed_{0}; + + friend class EcmaRuntimeStat; +}; +} // namespace panda::ecmascript +#endif diff --git a/ecmascript/vmstat/runtime_stat.cpp b/ecmascript/vmstat/runtime_stat.cpp new file mode 100644 index 0000000000000000000000000000000000000000..711678e49a37d5d30af616353a1c8d5b26486fdc --- /dev/null +++ b/ecmascript/vmstat/runtime_stat.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "runtime_stat.h" + +#include + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/runtime_call_id.h" + +namespace panda::ecmascript { +// NOLINTNEXTLINE(modernize-avoid-c-arrays) +EcmaRuntimeStat::EcmaRuntimeStat(const char * const runtimeCallerNames[], int count) +{ + for (int i = 0; i < count; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + callerStat_.emplace_back(PandaRuntimeCallerStat(CString(runtimeCallerNames[i]))); + } +} + +void EcmaRuntimeStat::StartCount(PandaRuntimeTimer *timer, int callerId) +{ + if (currentTimer_ != nullptr) { + timer->SetParent(currentTimer_); + } + PandaRuntimeTimer *parent = currentTimer_; + currentTimer_ = timer; + PandaRuntimeCallerStat *callerStat = &callerStat_[callerId]; + timer->Start(callerStat, parent); +} + +void EcmaRuntimeStat::StopCount(const PandaRuntimeTimer *nowTimer) +{ + if (nowTimer != currentTimer_) { + return; + } + PandaRuntimeTimer *parentTimer = currentTimer_->Stop(); + currentTimer_ = parentTimer; +} + +void EcmaRuntimeStat::Print() const +{ + if (currentTimer_ != nullptr) { + currentTimer_->Snapshot(); + } + LOG_ECMA(INFO) << GetAllStats(); +} + +void EcmaRuntimeStat::ResetAllCount() +{ + while (currentTimer_ != nullptr) { + StopCount(currentTimer_); + } + for (auto &runCallerStat : callerStat_) { + runCallerStat.Reset(); + } +} + +CString EcmaRuntimeStat::GetAllStats() const +{ + CStringStream statistic; + statistic << "panda runtime stat:" << std::endl; + static constexpr int nameRightAdjustment = 50; + static constexpr int numberRightAdjustment = 20; + statistic << std::right << std::setw(nameRightAdjustment) << "InterPreter && GC && C++ Builtin Function" + << std::setw(numberRightAdjustment) << "Time(ns)" << std::setw(numberRightAdjustment) << "Count" + << std::setw(numberRightAdjustment) << "MaxTime(ns)" + << std::setw(numberRightAdjustment) << "AverageTime(ns)" << std::endl; + + statistic << "===========================================================================================" + << "=======================================" << std::endl; + for (auto &runCallerStat : callerStat_) { + if (runCallerStat.TotalCount() != 0) { + statistic << std::right << std::setw(nameRightAdjustment) << runCallerStat.Name() + << std::setw(numberRightAdjustment) << runCallerStat.TotalTime() + << std::setw(numberRightAdjustment) << runCallerStat.TotalCount() + << std::setw(numberRightAdjustment) << runCallerStat.MaxTime() + << std::setw(numberRightAdjustment) << runCallerStat.TotalTime() / runCallerStat.TotalCount() + << std::endl; + } + } + return statistic.str(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/vmstat/runtime_stat.h b/ecmascript/vmstat/runtime_stat.h new file mode 100644 index 0000000000000000000000000000000000000000..15806f6e521014e27319f055f16fc3f877e06958 --- /dev/null +++ b/ecmascript/vmstat/runtime_stat.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_ECMA_RUNTIME_RUNTIME_STAT_H +#define PANDA_ECMA_RUNTIME_RUNTIME_STAT_H + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/vmstat/caller_stat.h" + +namespace panda::ecmascript { +class EcmaRuntimeStat { +public: + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + explicit EcmaRuntimeStat(const char * const runtimeCallerNames[], int count); + EcmaRuntimeStat() = default; + virtual ~EcmaRuntimeStat() = default; + + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(EcmaRuntimeStat); + DEFAULT_COPY_SEMANTIC(EcmaRuntimeStat); + + void StartCount(PandaRuntimeTimer *timer, int callerId); + void StopCount(const PandaRuntimeTimer *timer); + CString GetAllStats() const; + void ResetAllCount(); + void Print() const; + +private: + PandaRuntimeTimer *currentTimer_ = nullptr; + CVector callerStat_{}; +}; + +class RuntimeTimerScope { +public: + explicit RuntimeTimerScope(JSThread *thread, int callerId, EcmaRuntimeStat *stat) + { + bool statEnabled = thread->GetEcmaVM()->IsRuntimeStatEnabled(); + if (!statEnabled || stat == nullptr) { + return; + } + stats_ = stat; + stats_->StartCount(&timer_, callerId); + } + RuntimeTimerScope(const EcmaVM *vm, int callerId, EcmaRuntimeStat *stat) + { + bool statEnabled = vm->IsRuntimeStatEnabled(); + if (!statEnabled || stat == nullptr) { + return; + } + stats_ = stat; + stats_->StartCount(&timer_, callerId); + } + ~RuntimeTimerScope() + { + if (stats_ != nullptr) { + stats_->StopCount(&timer_); + } + } + NO_COPY_SEMANTIC(RuntimeTimerScope); + NO_MOVE_SEMANTIC(RuntimeTimerScope); + +private: + PandaRuntimeTimer timer_{}; + EcmaRuntimeStat *stats_{nullptr}; +}; +} // namespace panda::ecmascript +#endif diff --git a/ecmascript/weak_vector-inl.h b/ecmascript/weak_vector-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..f2a85dcbc3ad8e0556a9c839c1e3b95c7185279f --- /dev/null +++ b/ecmascript/weak_vector-inl.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_WEAK_VECTOR_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_WEAK_VECTOR_INL_H + +#include "weak_vector.h" +#include "tagged_array-inl.h" + +namespace panda::ecmascript { +array_size_t WeakVector::GetEnd() const +{ + return TaggedArray::Get(END_INDEX).GetArrayLength(); +} + +bool WeakVector::Full() const +{ + return GetEnd() == GetCapacity(); +} + +bool WeakVector::Empty() const +{ + return GetEnd() == 0; +} + +array_size_t WeakVector::GetCapacity() const +{ + return TaggedArray::GetLength() - ELEMENTS_START_INDEX; +} + +JSTaggedValue WeakVector::Get(array_size_t index) const +{ + ASSERT(index < GetCapacity()); + return TaggedArray::Get(VectorToArrayIndex(index)); +} + +void WeakVector::Set(const JSThread *thread, array_size_t index, JSTaggedValue value) +{ + ASSERT(index < GetCapacity()); + return TaggedArray::Set(thread, VectorToArrayIndex(index), value); +} + +void WeakVector::SetEnd(const JSThread *thread, array_size_t end) +{ + ASSERT(end <= GetCapacity()); + TaggedArray::Set(thread, END_INDEX, JSTaggedValue(end)); +} +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_WEAK_VECTOR_INL_H \ No newline at end of file diff --git a/ecmascript/weak_vector.cpp b/ecmascript/weak_vector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2809a115f6c678de182f0a4faa90691bb86b3404 --- /dev/null +++ b/ecmascript/weak_vector.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "weak_vector.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/weak_vector-inl.h" + +namespace panda::ecmascript { +JSHandle WeakVector::Create(const JSThread *thread, array_size_t capacity) +{ + ASSERT(capacity < MAX_VECTOR_INDEX); + + array_size_t length = VectorToArrayIndex(capacity); + JSHandle vector = JSHandle(thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length)); + + vector->SetEnd(thread, 0); + return vector; +} + +bool WeakVector::Delete(const JSThread *thread, array_size_t index) +{ + array_size_t end = GetEnd(); + if (index < end) { + Set(thread, index, JSTaggedValue::Hole()); + return true; + } + return false; +} + +JSHandle WeakVector::Grow(const JSThread *thread, const JSHandle &old, array_size_t newCapacity) +{ + array_size_t oldCapacity = old->GetCapacity(); + ASSERT(newCapacity > oldCapacity); + if (oldCapacity == MAX_VECTOR_INDEX) { + return old; + } + + if (newCapacity > MAX_VECTOR_INDEX) { + newCapacity = MAX_VECTOR_INDEX; + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newVec = factory->CopyArray(JSHandle(old), VectorToArrayIndex(oldCapacity), + VectorToArrayIndex(newCapacity)); + + return JSHandle(newVec); +} + +array_size_t WeakVector::PushBack(const JSThread *thread, JSTaggedValue value) +{ + array_size_t end = GetEnd(); + if (end == GetCapacity()) { + return TaggedArray::MAX_ARRAY_INDEX; + } + + Set(thread, end, value); + SetEnd(thread, end + 1); + return end; +} +} // namespace panda::ecmascript diff --git a/ecmascript/weak_vector.h b/ecmascript/weak_vector.h new file mode 100644 index 0000000000000000000000000000000000000000..73e357e0d23df4a69445160f4a983f671742aca5 --- /dev/null +++ b/ecmascript/weak_vector.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_WEAK_VECTOR_H +#define PANDA_RUNTIME_ECMASCRIPT_WEAK_VECTOR_H + +#include "ecmascript/js_handle.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/tagged_array.h" + +namespace panda::ecmascript { +class WeakVector : public TaggedArray { +public: + static WeakVector *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static constexpr array_size_t DEFALUT_CAPACITY = 4; + static JSHandle Create(const JSThread *thread, array_size_t capacity = DEFALUT_CAPACITY); + static JSHandle Grow(const JSThread *thread, const JSHandle &old, array_size_t newCapacity); + array_size_t PushBack(const JSThread *thread, JSTaggedValue value); + // just set index value to Hole + bool Delete(const JSThread *thread, array_size_t index); + + inline array_size_t GetEnd() const; + + inline bool Full() const; + + inline bool Empty() const; + + inline array_size_t GetCapacity() const; + + inline JSTaggedValue Get(array_size_t index) const; + + inline void Set(const JSThread *thread, array_size_t index, JSTaggedValue value); + +private: + static const array_size_t MIN_CAPACITY = 2; + static const array_size_t END_INDEX = 0; + static const array_size_t ELEMENTS_START_INDEX = 1; + static const array_size_t MAX_VECTOR_INDEX = TaggedArray::MAX_ARRAY_INDEX - ELEMENTS_START_INDEX; + + inline static constexpr array_size_t VectorToArrayIndex(array_size_t index) + { + return index + ELEMENTS_START_INDEX; + } + + inline void SetEnd(const JSThread *thread, array_size_t end); +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_WEAK_VECTOR_H diff --git a/js_runtime_config.gni b/js_runtime_config.gni new file mode 100644 index 0000000000000000000000000000000000000000..e84b65566cfdab7f623b2c66c703be50a6092046 --- /dev/null +++ b/js_runtime_config.gni @@ -0,0 +1,19 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ark_root = "//ark/runtime_core" +js_root = "//ark/js_runtime" + +# For OpenHarmony build, always link with the static lib: +sdk_libc_secshared_dep = "//utils/native/base:utilsecurec" +sdk_libc_secshared_config = "//utils/native/base:utils_config" diff --git a/ohos.build b/ohos.build new file mode 100644 index 0000000000000000000000000000000000000000..3782b5c069197f21f153fe5f7a13f992cb4922a3 --- /dev/null +++ b/ohos.build @@ -0,0 +1,20 @@ +{ + "subsystem": "ark", + "parts": { + "ark_js_runtime": { + "variants": [ + "phone" + ], + "module_list": [ + "//ark/js_runtime:ark_js_packages", + "//ark/js_runtime:ark_js_host_linux_tools_packages" + ], + "inner_kits": [], + "system_kits": [], + "test_list": [ + "//ark/js_runtime:ark_js_unittest", + "//ark/js_runtime:ark_js_host_unittest" + ] + } + } +} diff --git a/run_test262.sh b/run_test262.sh new file mode 100755 index 0000000000000000000000000000000000000000..82bb8468a61ad9ce2e62b3ef32c338c6a63adb33 --- /dev/null +++ b/run_test262.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +pushd ark/ts2abc + time=`date +'%Y%m%d%H%M%S'` + if [ ! -d report ];then + mkdir report + fi + + python3 test262/run_test262.py --es2015 all --libs-dir ../../out/ohos-arm-release/clang_x64/ark/ark:../../out/ohos-arm-release/clang_x64/ark/ark_js_runtime:../../out/ohos-arm-release/clang_x64/global/i18n_standard:../../prebuilts/clang/ohos/linux-x86_64/llvm/lib --ark-tool=../../out/ohos-arm-release/clang_x64/ark/ark_js_runtime/ark_js_vm --ark-frontend-tool=../../out/ohos-arm-release/clang_x64/ark/ark/build/src/index.js + + if [ $? -ne 0 ];then + echo 'execute run_test262.py failed!' + exit 1; + fi + + if [ ! -f out/test262/result.txt ];then + echo 'The result.txt file of test262 is not produced!' + exit 1; + fi + + cp out/test262/result.txt report/result_es2015_${time}.txt + + pushd report + es2015_fail=`grep FAIL result_es2015_${time}.txt | wc -l` + threshold=6 + if [ ${es2015_fail} -gt ${threshold} ];then + echo 'test262 fail case over thresgold' + exit 1; + else + exit 0; + fi + popd +popd diff --git a/test/copy_resource.py b/test/copy_resource.py new file mode 100755 index 0000000000000000000000000000000000000000..b6b3a10cd18d1b2c669cb36a867bc7b16d0e2755 --- /dev/null +++ b/test/copy_resource.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +#coding: utf-8 + +""" +Copyright (c) 2021 Huawei Device Co., Ltd. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +""" +Description: Copy test resource file to correct path +""" + +import os +import argparse +import shutil + + +def parse_args(): + """parse arguments.""" + parser = argparse.ArgumentParser() + parser.add_argument('--src-path', help='test xml source path') + parser.add_argument('--src-xml', help='test xml source file') + parser.add_argument('--dst-path', help='the copy dest path') + args = parser.parse_args() + return args + + +def copy_xml(args): + """copy resource xml to test direction.""" + src_xml_file = os.path.join(args.src_path, args.src_xml) + dst_xml_file = os.path.join(args.dst_path, args.src_xml) + + if not os.path.isfile(src_xml_file): + print(args.src_xml + " not exist.") + return + + if not os.path.exists(args.dst_path): + os.makedirs(args.dst_path) + + shutil.copyfile(src_xml_file, dst_xml_file) + + +if __name__ == '__main__': + input_args = parse_args() + copy_xml(input_args) diff --git a/test/moduletest/BUILD.gn b/test/moduletest/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..d93fd0a18e5f59d3528cc754f48faa19d69ff564 --- /dev/null +++ b/test/moduletest/BUILD.gn @@ -0,0 +1,34 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +group("ark_js_moduletest") { + testonly = true + deps = [ + "async:async_test(${host_toolchain})", + "bitwiseop:bitwiseop_test(${host_toolchain})", + "class:class_test(${host_toolchain})", + "dyninstruction:dyninstruction_test(${host_toolchain})", + "fortest:fortest_test(${host_toolchain})", + "generator:generator_test(${host_toolchain})", + "getunmappedargs:getunmappedargs_test(${host_toolchain})", + "helloworld:helloworld_test(${host_toolchain})", + "lexicalenv:lexicalenv_test(${host_toolchain})", + + # "module:module_test(${host_toolchain})", + "multiargs:multiargs_test(${host_toolchain})", + "newobjdynrange:newobjdynrange_test(${host_toolchain})", + "promise:promise_test(${host_toolchain})", + "throwdyn:throwdyn_test(${host_toolchain})", + "yieldstar:yieldstar_test(${host_toolchain})", + ] +} diff --git a/test/moduletest/async/BUILD.gn b/test/moduletest/async/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..32d09ada3d9fe77db0a9a88da9b3e8c5899e7218 --- /dev/null +++ b/test/moduletest/async/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("async") { + deps = [] +} diff --git a/test/moduletest/async/async.js b/test/moduletest/async/async.js new file mode 100644 index 0000000000000000000000000000000000000000..afa51d7efdee3524f93029fd3a47471bc25a00fc --- /dev/null +++ b/test/moduletest/async/async.js @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +async function foo() { + var a = await 1 + print(a) +} + +var s = foo() +s.then(msg=>{ + print(msg) +}) +print("main over"); \ No newline at end of file diff --git a/test/moduletest/async/expect_output.txt b/test/moduletest/async/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..3f92fc889937db1d56f20d277027a5cb8ad3ca07 --- /dev/null +++ b/test/moduletest/async/expect_output.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +main over +1 +undefined diff --git a/test/moduletest/bitwiseop/BUILD.gn b/test/moduletest/bitwiseop/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..ae74dadae906cab147e8150ad7ca8f4d7bedfc4b --- /dev/null +++ b/test/moduletest/bitwiseop/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("bitwiseop") { + deps = [] +} diff --git a/test/moduletest/bitwiseop/bitwiseop.js b/test/moduletest/bitwiseop/bitwiseop.js new file mode 100644 index 0000000000000000000000000000000000000000..17074c69625739c4678c26dc971535ff73cba8e9 --- /dev/null +++ b/test/moduletest/bitwiseop/bitwiseop.js @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +print(13 & 3) +print(13 | 3) +print(13 ^ 3) +print(13 << 3) +print(13 >> 3) +print(13 >>> 3) +print(~13) \ No newline at end of file diff --git a/test/moduletest/bitwiseop/expect_output.txt b/test/moduletest/bitwiseop/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..39ca50ff1355cbc46a07081c3893aad5d32362ad --- /dev/null +++ b/test/moduletest/bitwiseop/expect_output.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +1 +15 +14 +104 +1 +1 +-14 diff --git a/test/moduletest/class/BUILD.gn b/test/moduletest/class/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..a0bc9d8a6218e0a82ccf28db233315965e8d4481 --- /dev/null +++ b/test/moduletest/class/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("class") { + deps = [] +} diff --git a/test/moduletest/class/class.js b/test/moduletest/class/class.js new file mode 100644 index 0000000000000000000000000000000000000000..a5c20f68e264b65947180162ca76220648d70725 --- /dev/null +++ b/test/moduletest/class/class.js @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Parent { + constructor(x) { + this.x = x; + } + + static toString() { + return 'parent'; + } +} + +class Child extends Parent { + constructor(x, y) { + super(x); + this.y = y; + } + + value() { + return this.x * this.y; + } + + static toString() { + return super.toString() + ' child'; + } +} + +var c = new Child(2, 3); +print(c.value()); +print(Child.toString()); \ No newline at end of file diff --git a/test/moduletest/class/expect_output.txt b/test/moduletest/class/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..6b506e71e8a1bb9cccb7335bb380759f543a1851 --- /dev/null +++ b/test/moduletest/class/expect_output.txt @@ -0,0 +1,15 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +6 +parent child diff --git a/test/moduletest/dyninstruction/BUILD.gn b/test/moduletest/dyninstruction/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..0d2756e575a4ce20044ed1c86e623f029008398d --- /dev/null +++ b/test/moduletest/dyninstruction/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("dyninstruction") { + deps = [] +} diff --git a/test/moduletest/dyninstruction/dyninstruction.js b/test/moduletest/dyninstruction/dyninstruction.js new file mode 100644 index 0000000000000000000000000000000000000000..f275ba281581a1a4d997f89369b419777900d6f8 --- /dev/null +++ b/test/moduletest/dyninstruction/dyninstruction.js @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function Foo() { + print(3) + print(1.1) + return 1.1 +} +print(Foo()) +print(9) \ No newline at end of file diff --git a/test/moduletest/dyninstruction/expect_output.txt b/test/moduletest/dyninstruction/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..90deb2d840d6f789924908796a53d1393a04032a --- /dev/null +++ b/test/moduletest/dyninstruction/expect_output.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +3 +1.1 +1.1 +9 diff --git a/test/moduletest/fortest/BUILD.gn b/test/moduletest/fortest/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..23804e2c6d3eeba2442273d91e36f21eaa00ba26 --- /dev/null +++ b/test/moduletest/fortest/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("fortest") { + deps = [] +} diff --git a/test/moduletest/fortest/expect_output.txt b/test/moduletest/fortest/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..3b9bff4a9296e48610bf6f41e5cf98ee3de41363 --- /dev/null +++ b/test/moduletest/fortest/expect_output.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +* +** +*** +**** +***** +****** +******* +******** +********* +********** +10 +9 +8 +7 +6 +5 +4 +3 +2 +1 diff --git a/test/moduletest/fortest/fortest.js b/test/moduletest/fortest/fortest.js new file mode 100644 index 0000000000000000000000000000000000000000..3412064be1ea20298beb333c11766ef06bcbeac8 --- /dev/null +++ b/test/moduletest/fortest/fortest.js @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +let a = '*' +let s = '' + +for (let i = 0; i < 10; i++) +{ + s += a + print(s) +} + +let i = 10 + +while (i > 0) { + print(i); + i--; +} + diff --git a/test/moduletest/generator/BUILD.gn b/test/moduletest/generator/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..2f59ff19fcddeb7befe2cbf2942f258faa795e30 --- /dev/null +++ b/test/moduletest/generator/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("generator") { + deps = [] +} diff --git a/test/moduletest/generator/expect_output.txt b/test/moduletest/generator/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..04cc106ed6df4c65df098c7fc13a714d404af502 --- /dev/null +++ b/test/moduletest/generator/expect_output.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +1 false +2 false +undefined true +undefined true diff --git a/test/moduletest/generator/generator.js b/test/moduletest/generator/generator.js new file mode 100644 index 0000000000000000000000000000000000000000..7b016a30df49212882c2fa8a3305747ccfd3dcd8 --- /dev/null +++ b/test/moduletest/generator/generator.js @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function *foo() { + yield 1 + yield 2 +} + +var p = foo() +var a = p.next() +print(a.value, a.done) +var b = p.next() +print(b.value, b.done) +var c = p.next() +print(c.value, c.done) +var d = p.next() +print(d.value, d.done) \ No newline at end of file diff --git a/test/moduletest/getunmappedargs/BUILD.gn b/test/moduletest/getunmappedargs/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..414f2a9778205f46b05f4345c11a2d8e407c58e4 --- /dev/null +++ b/test/moduletest/getunmappedargs/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("getunmappedargs") { + deps = [] +} diff --git a/test/moduletest/getunmappedargs/expect_output.txt b/test/moduletest/getunmappedargs/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..cce773ec102f48ce083a3f5ec01fb6533b147a21 --- /dev/null +++ b/test/moduletest/getunmappedargs/expect_output.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +1 +undefined +undefined diff --git a/test/moduletest/getunmappedargs/getunmappedargs.js b/test/moduletest/getunmappedargs/getunmappedargs.js new file mode 100644 index 0000000000000000000000000000000000000000..142e9633e15c41e5a81cfd36a38801816e7add73 --- /dev/null +++ b/test/moduletest/getunmappedargs/getunmappedargs.js @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function Foo() { + print(arguments[0]) + print(arguments[1]) + print(arguments[2]) +} + +Foo(1) \ No newline at end of file diff --git a/test/moduletest/helloworld/BUILD.gn b/test/moduletest/helloworld/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..1f9c33ffa37a7ff67e29ea4b154ae6de22e26e67 --- /dev/null +++ b/test/moduletest/helloworld/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("helloworld") { + deps = [] +} diff --git a/test/moduletest/helloworld/expect_output.txt b/test/moduletest/helloworld/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..d53f276158d8a037f602c28faac1bad1c36ea646 --- /dev/null +++ b/test/moduletest/helloworld/expect_output.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +hello world ! diff --git a/test/moduletest/helloworld/helloworld.js b/test/moduletest/helloworld/helloworld.js new file mode 100644 index 0000000000000000000000000000000000000000..b41a5e95bf3dd92278d5d80fa115d745a0684843 --- /dev/null +++ b/test/moduletest/helloworld/helloworld.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +print("hello world !") \ No newline at end of file diff --git a/test/moduletest/lexicalenv/BUILD.gn b/test/moduletest/lexicalenv/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..8700e03c9eaa97c19e5f31f188d128f76330767b --- /dev/null +++ b/test/moduletest/lexicalenv/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("lexicalenv") { + deps = [] +} diff --git a/test/moduletest/lexicalenv/expect_output.txt b/test/moduletest/lexicalenv/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..1734c3781e76448a6ab57fcf9ac6e5a0583d358b --- /dev/null +++ b/test/moduletest/lexicalenv/expect_output.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +1 2 3 diff --git a/test/moduletest/lexicalenv/lexicalenv.js b/test/moduletest/lexicalenv/lexicalenv.js new file mode 100644 index 0000000000000000000000000000000000000000..bf8c8c7906ec0600b579de6a56955ee158ff2f1e --- /dev/null +++ b/test/moduletest/lexicalenv/lexicalenv.js @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function foo(a) { + var b = 2; + var c = 3 + function bar() { + print(a, b, c) + } + bar() +} + +foo(1) diff --git a/test/moduletest/module/B.js b/test/moduletest/module/B.js new file mode 100644 index 0000000000000000000000000000000000000000..78bbfad91491fc93e32c2f8d6da3b1ea6b23cf3c --- /dev/null +++ b/test/moduletest/module/B.js @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + export { Car }; + export * from './C.js'; + + var Car = { + carInfo: function() { + return this.name + ":" + this.type + ":" + this.price; + } + } + + var myCar = { + name: "HwCar", + type: "HW", + price: "CNY:XXW" + } + +var info = Car.carInfo.apply(myCar); diff --git a/test/moduletest/module/BUILD.gn b/test/moduletest/module/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..fbf4dea604bd2bf0419d0fdd4efad43e1f033682 --- /dev/null +++ b/test/moduletest/module/BUILD.gn @@ -0,0 +1,29 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("B") { + deps = [] +} + +host_moduletest_action("C") { + deps = [] +} + +host_moduletest_action("module") { + deps = [ + ":gen_B_abc", + ":gen_C_abc", + ] +} diff --git a/test/moduletest/module/C.js b/test/moduletest/module/C.js new file mode 100644 index 0000000000000000000000000000000000000000..9ea633d8153052402f7d3e8059c07e785af30731 --- /dev/null +++ b/test/moduletest/module/C.js @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var Star = { + starColor: function() { + return this.name + ":" + this.type + ":" + this.color; + } +} + +export { Star }; + +var myStar = { + name: "Polaris", + type: "FixedStar", + color: "Yellow" + } + +var info = Star.starColor.apply(myStar); diff --git a/test/moduletest/module/expect_output.txt b/test/moduletest/module/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..5f979ad0da563f4b24b13996d6134396ec98d8a9 --- /dev/null +++ b/test/moduletest/module/expect_output.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Pass!! diff --git a/test/moduletest/module/module.js b/test/moduletest/module/module.js new file mode 100644 index 0000000000000000000000000000000000000000..c9a565653797cea22b6b38e1ccd7f1b9a002c49d --- /dev/null +++ b/test/moduletest/module/module.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Car } from './B.js'; // Test direct Export & use after import +import { Star } from './C.js'; // Test indirect Export & use after import + +let A = Car; + +var myCar = { + name: "HWCar_Test", + type: "HW_Test", + price: "CNY:XXW_Test" + } + +var infoA = A.carInfo.apply(myCar); + +let C = Star; + + var myStar = { + name: "Polaris_Test", + type: "fixedStar_Test", + color: "Yellow_Test" + } + +var infoC = Star.starColor.apply(myStar); + +if (infoA != "HWCar_Test:HW_Test:CNY:XXW_Test" ) { + print("Direct Export Fail"); +} else if (infoC != "Polaris_Test:fixedStar_Test:Yellow_Test") { + print("Indirect Export Fail"); +} else { + print("Pass!!"); +} + diff --git a/test/moduletest/multiargs/BUILD.gn b/test/moduletest/multiargs/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..11f4b71b06ff7ac217e4af7fa49a5a23c7a4e7b3 --- /dev/null +++ b/test/moduletest/multiargs/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("multiargs") { + deps = [] +} diff --git a/test/moduletest/multiargs/expect_output.txt b/test/moduletest/multiargs/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..3d20c0cbfa10e9fab75d3cea7940d1cc0e10e510 --- /dev/null +++ b/test/moduletest/multiargs/expect_output.txt @@ -0,0 +1,22 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + 0 + 000 + 00000 +0000000 +123456789 +hello, world +aaabbbccc +111222333666 +54321 diff --git a/test/moduletest/multiargs/multiargs.js b/test/moduletest/multiargs/multiargs.js new file mode 100644 index 0000000000000000000000000000000000000000..38a14f6f80bbf22983e68c33e05e6c24290e7460 --- /dev/null +++ b/test/moduletest/multiargs/multiargs.js @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function zero() +{ + var a = ' 0\n' + var b = ' 000\n' + var c = ' 00000\n' + var d = '0000000' + print(a+b+c+d) +} + +function one(x) +{ + print(x) +} + +function two(x,y) +{ + print(x+y) +} + +function three(x,y,z) +{ + print(x+y+z) +} + +function four(x,y,z,t) +{ + print(x+y+z+t) +} + +function five(x,y,z,t,a) +{ + let s = x + 10*y+ 100*z + 1000*t + 10000*a + print(s.toString(10)) +} + +zero() +one(123456789) +two('hello,',' world') +three('aaa','bbb','ccc') + +let x = 111 +let y = 222 +let z = 333 +let a = 666 + +four(x.toString(10),y.toString(10),z.toString(10),a.toString(10)) +five(1,2,3,4,5) \ No newline at end of file diff --git a/test/moduletest/newobjdynrange/BUILD.gn b/test/moduletest/newobjdynrange/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..282265e4a14bb5fbcf15d22accd5de6fbfdc5097 --- /dev/null +++ b/test/moduletest/newobjdynrange/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("newobjdynrange") { + deps = [] +} diff --git a/test/moduletest/newobjdynrange/expect_output.txt b/test/moduletest/newobjdynrange/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..222a6d1528a9adabbe2ed0d4c8ccfb314c2519bc --- /dev/null +++ b/test/moduletest/newobjdynrange/expect_output.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +true diff --git a/test/moduletest/newobjdynrange/newobjdynrange.js b/test/moduletest/newobjdynrange/newobjdynrange.js new file mode 100644 index 0000000000000000000000000000000000000000..09297c5f1418f2a41ed7b2f8d96ddf4f964bff0b --- /dev/null +++ b/test/moduletest/newobjdynrange/newobjdynrange.js @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function foo(arg1, arg2) { + this.arg1 = arg1 + this.arg2 = arg2 +} +var p = new foo("arg1", "arg2") +print(p.arg1 === "arg1" && p.arg2 === "arg2") \ No newline at end of file diff --git a/test/moduletest/promise/BUILD.gn b/test/moduletest/promise/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..39ca8075253328b370c909e4900a83b949814642 --- /dev/null +++ b/test/moduletest/promise/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("promise") { + deps = [] +} diff --git a/test/moduletest/promise/expect_output.txt b/test/moduletest/promise/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..441004cf5ca1d32fbcf128015931e5ec11dea21e --- /dev/null +++ b/test/moduletest/promise/expect_output.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +reject +1357 +catch +1357 +resolve +1479,2468 diff --git a/test/moduletest/promise/promise.js b/test/moduletest/promise/promise.js new file mode 100644 index 0000000000000000000000000000000000000000..986cd41675d1fb8c49e625e772b0ade5352b2693 --- /dev/null +++ b/test/moduletest/promise/promise.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var p = new Promise((resolve, reject) => { + resolve(1479); +}) +var p1 = Promise.reject(1357); +var p2 = Promise.resolve(2468); +var p3 = Promise.race([p1, p2]); +p3.then( + (value) => { + print("resolve"); + print(value); + }, + (value) => { + print("reject"); + print(value); + } +) + +p3.catch((value) => { + print("catch"); + print(value); +}) + +var p4 = Promise.all([p, p2]); +p4.then( + (value) => { + print("resolve"); + print(value); + }, + (value) => { + print("reject"); + print(value); + } +) diff --git a/test/moduletest/throwdyn/BUILD.gn b/test/moduletest/throwdyn/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..100002dc692b342198cb7e9c08c0f01aa5055274 --- /dev/null +++ b/test/moduletest/throwdyn/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("throwdyn") { + deps = [] +} diff --git a/test/moduletest/throwdyn/expect_output.txt b/test/moduletest/throwdyn/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..ba62f77291a7d59b72e1a4d071dc9ee8e337ec66 --- /dev/null +++ b/test/moduletest/throwdyn/expect_output.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +sub_error diff --git a/test/moduletest/throwdyn/throwdyn.js b/test/moduletest/throwdyn/throwdyn.js new file mode 100644 index 0000000000000000000000000000000000000000..408cd0d4d33a206f63982294d20a1f57c056675c --- /dev/null +++ b/test/moduletest/throwdyn/throwdyn.js @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function throwError() { + throw "sub_error" +} + +function catchSubError() { + try { + throwError() + } catch (e) { + print(e) + } +} + +catchSubError() diff --git a/test/moduletest/yieldstar/BUILD.gn b/test/moduletest/yieldstar/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..33ee4121885661fc95938f45581aa9294694067d --- /dev/null +++ b/test/moduletest/yieldstar/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("yieldstar") { + deps = [] +} diff --git a/test/moduletest/yieldstar/expect_output.txt b/test/moduletest/yieldstar/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..b3715bf0b5ae679e9cf725472615c78b33117fe9 --- /dev/null +++ b/test/moduletest/yieldstar/expect_output.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +1 false +2 false +undefined true diff --git a/test/moduletest/yieldstar/yieldstar.js b/test/moduletest/yieldstar/yieldstar.js new file mode 100644 index 0000000000000000000000000000000000000000..77331cd8353abca27d06c788168d7ea7ef4d715e --- /dev/null +++ b/test/moduletest/yieldstar/yieldstar.js @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function *foo1() { + yield 1 + yield 2 +} + +function *foo2() { + yield *foo1() +} + +var p = foo2() +var a = p.next() +print(a.value, a.done) +var b = p.next() +print(b.value, b.done) +var c = p.next() +print(c.value, c.done) \ No newline at end of file diff --git a/test/resource/js_runtime/ohos_test.xml b/test/resource/js_runtime/ohos_test.xml new file mode 100644 index 0000000000000000000000000000000000000000..e46d8605ffdf83ec451c8d628378135e2f9da98b --- /dev/null +++ b/test/resource/js_runtime/ohos_test.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/test/run_ark_executable.py b/test/run_ark_executable.py new file mode 100755 index 0000000000000000000000000000000000000000..604917c871e63a4061265c49faf26eb1e7eae56c --- /dev/null +++ b/test/run_ark_executable.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +#coding: utf-8 + +""" +Copyright (c) 2021 Huawei Device Co., Ltd. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +""" +Description: run script + expect_output will get run result, expect_file will get print string +""" + +import argparse +import subprocess +import time + + +def parse_args(): + """parse arguments.""" + parser = argparse.ArgumentParser() + parser.add_argument('--script-file', help='execute script file') + parser.add_argument('--script-args', help='args of script') + parser.add_argument('--expect-output', help='expect output') + parser.add_argument('--expect-file', help='expect file') + parser.add_argument('--env-path', help='LD_LIBRARY_PATH env') + args = parser.parse_args() + return args + + +def judge_output(args): + """run testcase and judge is success or not.""" + start_time = time.time() + cmd = input_args.script_file + if input_args.script_args: + cmd += " " + input_args.script_args + subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, + env={'LD_LIBRARY_PATH': str(input_args.env_path)}) + try: + out, err = subp.communicate(timeout=120) # units: s + except TimeoutExpired: + subp.kill() + out, err = subp.communicate() + + if args.expect_output: + returncode = str(subp.returncode) + if returncode != args.expect_output: + print_str = out.decode('UTF-8') + print(print_str) + raise RuntimeError("Run [" + cmd + "] failed!") + elif args.expect_file: + with open(args.expect_file, mode='r') as file: + # skip license header + expect_output = ''.join(file.readlines()[13:]) + file.close() + print_str = out.decode('UTF-8') + if print_str != expect_output: + raise RuntimeError("\n>>>>> Expect : [" + expect_output \ + + "]\n>>>>> But got: [" + print_str + "]") + else: + raise RuntimeError("Run [" + cmd + "] with no expect !") + + print("Run [" + cmd + "] success!") + print("used: %.5f seconds" % (time.time() - start_time)) + + +if __name__ == '__main__': + input_args = parse_args() + judge_output(input_args) diff --git a/test/test_helper.gni b/test/test_helper.gni new file mode 100644 index 0000000000000000000000000000000000000000..5cfa05eec28e0a6eb72bb6d6c59cb0bbdd782579 --- /dev/null +++ b/test/test_helper.gni @@ -0,0 +1,124 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/js_runtime_config.gni") +import("//ark/ts2abc/ts2panda/ts2abc_config.gni") +import("//build/ohos.gni") +import("//build/test.gni") + +if (is_standard_system) { + icu_path = "i18n_standard" +} else { + icu_path = "i18n" +} + +template("host_unittest_action") { + _target_name_ = "${target_name}" + _deps_ = invoker.deps + + _deps_ += [ "//ark/js_runtime:libark_jsruntime_static" ] + + # unittest for phone running + ohos_unittest(_target_name_) { + forward_variables_from(invoker, + "*", + [ + "target_name", + "deps", + ]) + deps = _deps_ + } + + is_host_test = get_label_info(_target_name_, "toolchain") == host_toolchain + if (is_host_test) { + _module_out_path_ = invoker.module_out_path + + # unittest for host running + action("${_target_name_}Action") { + testonly = true + + deps = [ ":${_target_name_}(${host_toolchain})" ] + + script = "//ark/js_runtime/test/run_ark_executable.py" + + args = [ + "--script-file", + rebase_path( + "$root_out_dir/tests/unittest/${_module_out_path_}/${_target_name_}"), + "--expect-output", + "0", + "--env-path", + rebase_path(root_out_dir) + "/ark/ark:" + rebase_path(root_out_dir) + + "/ark/ark_js_runtime:" + rebase_path(root_out_dir) + + "/global/${icu_path}:" + + rebase_path("//prebuilts/clang/ohos/linux-x86_64/llvm/lib/"), + ] + + inputs = [ + "$root_out_dir/tests/unittest/${_module_out_path_}/${_target_name_}", + ] + outputs = [ "$target_out_dir/${_target_name_}/" ] + } + } +} + +template("host_moduletest_action") { + _target_name_ = "${target_name}" + _deps_ = invoker.deps + + _test_js_path_ = "./${_target_name_}.js" + _test_abc_path_ = "$target_out_dir/${_target_name_}.abc" + _test_expect_path_ = "./expect_output.txt" + + ts2abc_gen_abc("gen_${_target_name_}_abc") { + extra_visibility = [ ":*" ] # Only targets in this file can depend on this. + extra_dependencies = _deps_ + src_js = rebase_path(_test_js_path_) + dst_file = rebase_path(_test_abc_path_) + extra_args = [ "--debug" ] + + in_puts = [ + _test_js_path_, + _test_expect_path_, + ] + out_puts = [ _test_abc_path_ ] + } + + action("${_target_name_}_test") { + testonly = true + deps = [ + ":gen_${_target_name_}_abc", + "//ark/js_runtime/ecmascript/js_vm:ark_js_vm(${host_toolchain})", + ] + + script = "//ark/js_runtime/test/run_ark_executable.py" + + args = [ + "--script-file", + rebase_path(root_out_dir) + "/ark/ark_js_runtime/ark_js_vm", + "--script-args", + rebase_path(_test_abc_path_) + " _GLOBAL::func_main_0", + "--expect-file", + rebase_path(_test_expect_path_), + "--env-path", + rebase_path(root_out_dir) + "/ark/ark:" + rebase_path(root_out_dir) + + "/ark/ark_js_runtime:" + rebase_path(root_out_dir) + + "/global/${icu_path}:" + + rebase_path("//prebuilts/clang/ohos/linux-x86_64/llvm/lib/"), + ] + + inputs = [ _test_abc_path_ ] + + outputs = [ "$target_out_dir/${_target_name_}/" ] + } +}