add ark js_runtime

Signed-off-by: wanyanglan <wanyanglan1@huawei.com>
Change-Id: Iac7851492f10c0ef1303c621febb65e5fdd22e41
This commit is contained in:
wanyanglan 2021-09-04 16:06:49 +08:00
parent ac58bbe578
commit 86cfe52253
615 changed files with 147707 additions and 63 deletions

14
.gitignore vendored Normal file
View File

@ -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

387
BUILD.gn Normal file
View File

@ -0,0 +1,387 @@
# Copyright (c) 2021 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT 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",
"-ggdb3",
]
}
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"
}
# force gc for testcase
ohos_static_library("libark_jsruntime_test") {
sources = ecma_source
sources += [
"ecmascript/tooling/interface/debugger_api.cpp",
"ecmascript/tooling/interface/js_debugger.cpp",
]
defines = [ "FORCE_GC_TEST" ]
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) {
deps += [ "$ark_root/runtime:libarkruntime_static" ]
} else {
deps += [ "$ark_root/runtime:libarkruntime" ]
}
output_extension = "a"
subsystem_name = "ark"
}

177
LICENSE Normal file
View File

@ -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

79
OAT.xml Normal file
View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="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.
-->
<!-- OAT(OSS Audit Tool) configuration guide:
basedir: Root dir, the basedir + project path is the real source file location.
licensefile:
1.If the project don't have "LICENSE" in root dir, please define all the license files in this project in , OAT will check license files according to this rule.
tasklist(only for batch mode):
1. task: Define oat check thread, each task will start a new thread.
2. task name: Only an name, no practical effect.
3. task policy: Default policy for projects under this task, this field is required and the specified policy must defined in policylist.
4. task filter: Default filefilter for projects under this task, this field is required and the specified filefilter must defined in filefilterlist.
5. task project: Projects to be checked, the path field define the source root dir of the project.
policyList:
1. policy: All policyitems will be merged to default OAT.xml rules, the name of policy doesn't affect OAT check process.
2. policyitem: The fields type, name, path, desc is required, and the fields rule, group, filefilter is optional,the default value is:
<policyitem type="" name="" path="" desc="" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter"/>
3. policyitem type:
"compatibility" is used to check license compatibility in the specified path;
"license" is used to check source license header in the specified path;
"copyright" is used to check source copyright header in the specified path;
"import" is used to check source dependency in the specified path, such as import ... ,include ...
"filetype" is used to check file type in the specified path, supported file types: archive, binary
"filename" is used to check whether the specified file exists in the specified path(support projectroot in default OAT.xml), supported file names: LICENSE, README, README.OpenSource
4. policyitem name: This field is used for define the license, copyright, "*" means match all, the "!" prefix means could not match this value. For example, "!GPL" means can not use GPL license.
5. policyitem path: This field is used for define the source file scope to apply this policyitem, the "!" prefix means exclude the files. For example, "!.*/lib/.*" means files in lib dir will be exclude while process this policyitem.
6. policyitem rule and group: These two fields are used together to merge policy results. "may" policyitems in the same group means any one in this group passed, the result will be passed.
7. policyitem filefilter: Used to bind filefilter which define filter rules.
8. filefilter: Filter rules, the type filename is used to filter file name, the type filepath is used to filter file path.
Note:If the text contains special characters, please escape them according to the following rules:
" == &gt;
& == &gt;
' == &gt;
< == &gt;
> == &gt;
-->
<configuration>
<oatconfig>
<policy name="defaultPolicy" desc="" >
<policyitem type="compatibility" name="Apache" path="ark/js_runtime" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter" desc=""/>
</policy>
<filefilterlist>
<filefilter name="binaryFileTypePolicyFilter" desc="二进制文件校验策略的过滤条件" >
<filteritem type="filepath" name="docs/figures/en-us_image_0000001149439242.png" desc="自造的二进制图片"/>
<filteritem type="filepath" name="docs/figures/zh-cn_image_0000001196712959.png" desc="自造的二进制图片"/>
<filteritem type="filepath" name="docs/figures/en-us_image_0000001196789343.png" desc="自造的二进制图片"/>
<filteritem type="filepath" name="docs/figures/en-us_image_0000001197967897.png" desc="自造的二进制图片"/>
<filteritem type="filepath" name="docs/figures/zh-cn_image_0000001196789343.png" desc="自造的二进制图片"/>
<filteritem type="filepath" name="docs/figures/zh-cn_image_0000001197967897.png" desc="自造的二进制图片"/>
<filteritem type="filepath" name="docs/figures/en-us_image_0000001197275269.png" desc="自造的二进制图片"/>
<filteritem type="filepath" name="docs/figures/en-us_image_0000001197967983.png" desc="自造的二进制图片"/>
<filteritem type="filepath" name="docs/figures/zh-cn_image_0000001197275269.png" desc="自造的二进制图片"/>
<filteritem type="filepath" name="docs/figures/zh-cn_image_0000001197967983.png" desc="自造的二进制图片"/>
</filefilter>
<filefilter name="copyrightPolicyFilter" desc="copyright文件头校验策略的过滤条件" >
</filefilter>
<filefilter name="defaultPolicyFilter" desc="根目录LICENSE文件校验策略的过滤条件xxx" >
</filefilter>
</filefilterlist>
</oatconfig>
</configuration>

View File

@ -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/)

View File

@ -1,39 +1,77 @@
# ark_js_runtime
# ARK JS Runtime Module<a name="EN-US_TOPIC_0000001183610495"></a>
#### 介绍
{**以下是 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<a name="section190813718209"></a>
#### 安装教程
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<a name="section161941989596"></a>
#### 参与贡献
```
/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<a name="section119744591305"></a>
* 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<a name="section137768191623"></a>
./build.sh --product-name Hi3516DV300 --build-target ark\_js\_runtime
### Available APIs<a name="section175841548124517"></a>
For details, see [NAPI](https://gitee.com/openharmony/ace_napi/blob/master/README.md).
### Usage Guidelines<a name="section129654513264"></a>
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<a name="section1371113476307"></a>
[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/)

77
README_zh.md Normal file
View File

@ -0,0 +1,77 @@
# 方舟JS运行时组件<a name="ZH-CN_TOPIC_0000001183610495"></a>
- [目录](#section161941989596)
- [约束](#section119744591305)
- [编译构建](#section137768191623)
- [接口说明](#section175841548124517)
- [使用说明](#section129654513264)
- [相关仓](#section1371113476307)
### 简介<a name="section190813718209"></a>
**方舟JS运行时ARK JavaScript Runtime**是OpenHarmony上JS应用使用的运行时。包含JS对象的分配器以及垃圾回收器GC、符合ECMAScript规范的标准库、用于运行ARK前端组件生成的方舟字节码ARK Bytecodeabc的解释器、用于存储隐藏类的内联缓存、方舟JS运行时对外的函数接口AFFI等模块。
**方舟JS运行时组件架构图**
![](docs/figures/zh-cn_image_0000001196712959.png)
## 目录<a name="section161941989596"></a>
```
/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 # 模块测试用例
```
## 约束<a name="section119744591305"></a>
* 仅支持运行方舟JS前端工具链\(ts2abc\)生成的方舟字节码文件
* 只支持ES2015标准和严格模式use strict)
* 不支持通过字符串动态创建函数(比如new Function("console.log(1);"))
## 编译构建<a name="section137768191623"></a>
./build.sh --product-name Hi3516DV300 --build-target ark\_js\_runtime
### 接口说明<a name="section175841548124517"></a>
NAPI接口说明参考[NAPI组件](https://gitee.com/openharmony/ace_napi/blob/master/README_zh.md)
### 使用说明<a name="section129654513264"></a>
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
## 相关仓<a name="section1371113476307"></a>
[方舟运行时子系统](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)

View File

@ -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)

View File

@ -0,0 +1,67 @@
# ARK Runtime Subsystem<a name="EN-US_TOPIC_0000001138852894"></a>
- [Introduction](#section11660541593)
- [Directory Structure](#section161941989596)
- [Note](#section18393638195820)
- [Repositories Involved](#section1371113476307)
## Introduction<a name="section11660541593"></a>
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<a name="section161941989596"></a>
```
/ark
├── js_runtime # JS runtime module
├── runtime_core # Runtime common module
└── ts2abc # JS front-end tool of ARK compiler
```
## Note<a name="section18393638195820"></a>
For details, see the note of each module.
## Repositories Involved<a name="section1371113476307"></a>
**[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)

302
docs/development-example.md Normal file
View File

@ -0,0 +1,302 @@
# Development Example<a name="EN-US_TOPIC_0000001128096218"></a>
- [HelloWorld](#section105987593810)
- [Performing Test Case Test262](#section118471435115815)
This section describes how to develop and test ARK runtime.
## HelloWorld<a name="section105987593810"></a>
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 <external>
.record _ESModuleMode {
u8 isModule
}
# ====================
# METHODS
.function any func_main_0_any_any_any_any_(any a0, any a1, any a2) <static> {
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<a name="section118471435115815"></a>
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**.
<a name="table11141827153017"></a>
<table><thead align="left"><tr id="row101462717303"><th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.1"><p id="p51552743010"><a name="p51552743010"></a><a name="p51552743010"></a>Option</p>
</th>
<th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.2"><p id="p11592710304"><a name="p11592710304"></a><a name="p11592710304"></a>Description</p>
</th>
</tr>
</thead>
<tbody><tr id="row2015172763014"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p171592710306"><a name="p171592710306"></a><a name="p171592710306"></a>--h, --help</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p13151527133011"><a name="p13151527133011"></a><a name="p13151527133011"></a>Displays help information.</p>
</td>
</tr>
<tr id="row1015527173015"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p1615182712308"><a name="p1615182712308"></a><a name="p1615182712308"></a>--dir DIR</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p9556101593120"><a name="p9556101593120"></a><a name="p9556101593120"></a>Specifies the directory to test.</p>
</td>
</tr>
<tr id="row1015112763020"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p1815182733012"><a name="p1815182733012"></a><a name="p1815182733012"></a>--file FILE</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1615627173019"><a name="p1615627173019"></a><a name="p1615627173019"></a>Specifies the file to test.</p>
</td>
</tr>
<tr id="row131515277307"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p111572716304"><a name="p111572716304"></a><a name="p111572716304"></a>--mode [{1, 2, 3}]</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1820821404711"><a name="p1820821404711"></a><a name="p1820821404711"></a>Specifies the mode, which can be any of the following:</p>
<a name="ul136633170477"></a><a name="ul136633170477"></a><ul id="ul136633170477"><li><strong id="b12807202010471"><a name="b12807202010471"></a><a name="b12807202010471"></a>1</strong>: default</li><li><strong id="b16343325154719"><a name="b16343325154719"></a><a name="b16343325154719"></a>2</strong>: strict mode only</li><li><strong id="b19742163854610"><a name="b19742163854610"></a><a name="b19742163854610"></a>3</strong>: default and strict modes</li></ul>
</td>
</tr>
<tr id="row1815112753020"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p2151927193015"><a name="p2151927193015"></a><a name="p2151927193015"></a>--es51</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1715312588115"><a name="p1715312588115"></a><a name="p1715312588115"></a>Runs Test262 ES5.1.</p>
</td>
</tr>
<tr id="row1915182703012"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p17151527133017"><a name="p17151527133017"></a><a name="p17151527133017"></a>--es2015 [{all, only}]</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p536992675017"><a name="p536992675017"></a><a name="p536992675017"></a>Runs Test262 ES2015.</p>
<p id="p205288299503"><a name="p205288299503"></a><a name="p205288299503"></a><strong id="b2052812914503"><a name="b2052812914503"></a><a name="b2052812914503"></a>all</strong>: all cases</p>
<p id="p1392723585014"><a name="p1392723585014"></a><a name="p1392723585014"></a><strong id="b15128193544910"><a name="b15128193544910"></a><a name="b15128193544910"></a>only</strong>: only ES2015</p>
</td>
</tr>
<tr id="row10924204611109"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p18924846111013"><a name="p18924846111013"></a><a name="p18924846111013"></a>--esnext</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p15495042191410"><a name="p15495042191410"></a><a name="p15495042191410"></a>Runs <strong id="b13144135817502"><a name="b13144135817502"></a><a name="b13144135817502"></a>Test262-ES.next</strong>.</p>
</td>
</tr>
<tr id="row5161145010105"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p716125071020"><a name="p716125071020"></a><a name="p716125071020"></a>--engine FILE</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p121612050181014"><a name="p121612050181014"></a><a name="p121612050181014"></a>Runs other engines and specifies binary files (such as d8, hermes, jsc, and qjs).</p>
</td>
</tr>
<tr id="row1325585931120"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p112561595112"><a name="p112561595112"></a><a name="p112561595112"></a>--babel</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p32561959111112"><a name="p32561959111112"></a><a name="p32561959111112"></a>Specifies whether to use Babel to convert code.</p>
</td>
</tr>
<tr id="row95230818126"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p12523158191210"><a name="p12523158191210"></a><a name="p12523158191210"></a>--timeout TIMEOUT</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p65233871210"><a name="p65233871210"></a><a name="p65233871210"></a>Specifies the test timeout period in ms.</p>
</td>
</tr>
<tr id="row474911612120"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p1274912166123"><a name="p1274912166123"></a><a name="p1274912166123"></a>--threads THREADS</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p4749121631210"><a name="p4749121631210"></a><a name="p4749121631210"></a>Specifies the number of concurrent threads.</p>
</td>
</tr>
<tr id="row561512363122"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p26152036191218"><a name="p26152036191218"></a><a name="p26152036191218"></a>--hostArgs HOSTARGS</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p156151636161215"><a name="p156151636161215"></a><a name="p156151636161215"></a>Specifies the command line parameters sent to the eshost.</p>
</td>
</tr>
<tr id="row77091648111210"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p18709164871213"><a name="p18709164871213"></a><a name="p18709164871213"></a>--ark-tool ARK_TOOL</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p16709194812126"><a name="p16709194812126"></a><a name="p16709194812126"></a>Specifies the binary tool of ARK runtime.</p>
</td>
</tr>
<tr id="row3767145231210"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p3767155201216"><a name="p3767155201216"></a><a name="p3767155201216"></a>--ark-frontend-tool ARK_FRONTEND_TOOL</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p4767195251220"><a name="p4767195251220"></a><a name="p4767195251220"></a>Specifies the ARK front-end conversion tool.</p>
</td>
</tr>
<tr id="row753817001311"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p553870111318"><a name="p553870111318"></a><a name="p553870111318"></a>--libs-dir LIBS_DIR</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p35384041313"><a name="p35384041313"></a><a name="p35384041313"></a>Specifies the set of .so dependency file paths, separated by colons (:).</p>
</td>
</tr>
<tr id="row08504716135"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p11851747161314"><a name="p11851747161314"></a><a name="p11851747161314"></a>--ark-frontend [{ts2panda, es2panda}]</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1085144712137"><a name="p1085144712137"></a><a name="p1085144712137"></a>Specifies the frontend.</p>
</td>
</tr>
</tbody>
</table>
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
```

View File

@ -0,0 +1,41 @@
# Environment Setup and Compilation<a name="EN-US_TOPIC_0000001174215863"></a>
- [Configuring the Environment](#section922419503415)
- [Compilation](#section1166711064317)
## Configuring the Environment<a name="section922419503415"></a>
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<a name="section1166711064317"></a>
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
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

53
docs/overview.md Normal file
View File

@ -0,0 +1,53 @@
# Overview<a name="EN-US_TOPIC_0000001174295771"></a>
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.

237
docs/using-the-toolchain.md Normal file
View File

@ -0,0 +1,237 @@
# Using the Toolchain<a name="EN-US_TOPIC_0000001128256014"></a>
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
```
<a name="table18706114344420"></a>
<table><thead align="left"><tr id="row19706343164411"><th class="cellrowborder" valign="top" width="15.29152915291529%" id="mcps1.1.6.1.1"><p id="p1970694310447"><a name="p1970694310447"></a><a name="p1970694310447"></a>Option</p>
</th>
<th class="cellrowborder" valign="top" width="6.830683068306829%" id="mcps1.1.6.1.2"><p id="p9548142314456"><a name="p9548142314456"></a><a name="p9548142314456"></a>Abbreviation</p>
</th>
<th class="cellrowborder" valign="top" width="44.34443444344434%" id="mcps1.1.6.1.3"><p id="p170614318449"><a name="p170614318449"></a><a name="p170614318449"></a>Description</p>
</th>
<th class="cellrowborder" valign="top" width="26.01260126012601%" id="mcps1.1.6.1.4"><p id="p1841257144811"><a name="p1841257144811"></a><a name="p1841257144811"></a>Value Range</p>
</th>
<th class="cellrowborder" valign="top" width="7.520752075207521%" id="mcps1.1.6.1.5"><p id="p15894191313495"><a name="p15894191313495"></a><a name="p15894191313495"></a>Default Value</p>
</th>
</tr>
</thead>
<tbody><tr id="row770684312444"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p0706154312447"><a name="p0706154312447"></a><a name="p0706154312447"></a>--modules</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p1654810236457"><a name="p1654810236457"></a><a name="p1654810236457"></a>-m</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p12451427144913"><a name="p12451427144913"></a><a name="p12451427144913"></a>Compiles JS files based on the module.</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p6415710488"><a name="p6415710488"></a><a name="p6415710488"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p389411314490"><a name="p389411314490"></a><a name="p389411314490"></a>-</p>
</td>
</tr>
<tr id="row8707114315446"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p7707134344416"><a name="p7707134344416"></a><a name="p7707134344416"></a>--debug-log</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p15481123194512"><a name="p15481123194512"></a><a name="p15481123194512"></a>-l</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p1988112617501"><a name="p1988112617501"></a><a name="p1988112617501"></a>Enables the log function.</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p34135724819"><a name="p34135724819"></a><a name="p34135724819"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p16894813204919"><a name="p16894813204919"></a><a name="p16894813204919"></a>-</p>
</td>
</tr>
<tr id="row1770734394411"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p11549171604510"><a name="p11549171604510"></a><a name="p11549171604510"></a>--dump-assembly</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p1654815237456"><a name="p1654815237456"></a><a name="p1654815237456"></a>-a</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p1024452794916"><a name="p1024452794916"></a><a name="p1024452794916"></a>Outputs a text ARK bytecode file.</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p184145774817"><a name="p184145774817"></a><a name="p184145774817"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p20894101319494"><a name="p20894101319494"></a><a name="p20894101319494"></a>-</p>
</td>
</tr>
<tr id="row17707643124417"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p1270714432449"><a name="p1270714432449"></a><a name="p1270714432449"></a>--debug</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p1548172334510"><a name="p1548172334510"></a><a name="p1548172334510"></a>-d</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p32437275490"><a name="p32437275490"></a><a name="p32437275490"></a>Provides debug information.</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p134185718488"><a name="p134185718488"></a><a name="p134185718488"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p5894613104916"><a name="p5894613104916"></a><a name="p5894613104916"></a>-</p>
</td>
</tr>
<tr id="row14707184314419"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p5707194311449"><a name="p5707194311449"></a><a name="p5707194311449"></a>--show-statistics</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p1954872319456"><a name="p1954872319456"></a><a name="p1954872319456"></a>-s</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p16224102744913"><a name="p16224102744913"></a><a name="p16224102744913"></a>Displays statistics about bytecodes.</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p154195704813"><a name="p154195704813"></a><a name="p154195704813"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p19894131304917"><a name="p19894131304917"></a><a name="p19894131304917"></a>-</p>
</td>
</tr>
<tr id="row768813216460"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p20688113244612"><a name="p20688113244612"></a><a name="p20688113244612"></a>--output</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p1468863218469"><a name="p1468863218469"></a><a name="p1468863218469"></a>-o</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p1468814324461"><a name="p1468814324461"></a><a name="p1468814324461"></a>Specifies the path of the output file.</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p12624825135117"><a name="p12624825135117"></a><a name="p12624825135117"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p1989431344920"><a name="p1989431344920"></a><a name="p1989431344920"></a>-</p>
</td>
</tr>
<tr id="row6445636154611"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p64451436124618"><a name="p64451436124618"></a><a name="p64451436124618"></a>--timeout</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p1445113611468"><a name="p1445113611468"></a><a name="p1445113611468"></a>-t</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p1044510362466"><a name="p1044510362466"></a><a name="p1044510362466"></a>Specifies the timeout threshold.</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p745572486"><a name="p745572486"></a><a name="p745572486"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p7894111310494"><a name="p7894111310494"></a><a name="p7894111310494"></a>-</p>
</td>
</tr>
<tr id="row1978841614720"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p0788141614716"><a name="p0788141614716"></a><a name="p0788141614716"></a>--help</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p207884169473"><a name="p207884169473"></a><a name="p207884169473"></a>-h</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p178821634716"><a name="p178821634716"></a><a name="p178821634716"></a>Displays help information.</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p1341757114819"><a name="p1341757114819"></a><a name="p1341757114819"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p208946134499"><a name="p208946134499"></a><a name="p208946134499"></a>-</p>
</td>
</tr>
<tr id="row14354103234714"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p1235410329479"><a name="p1235410329479"></a><a name="p1235410329479"></a>--bc-version</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p63541832124712"><a name="p63541832124712"></a><a name="p63541832124712"></a>-v</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p14354832134715"><a name="p14354832134715"></a><a name="p14354832134715"></a>Outputs the current bytecode version.</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p16410577487"><a name="p16410577487"></a><a name="p16410577487"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p14894201364911"><a name="p14894201364911"></a><a name="p14894201364911"></a>-</p>
</td>
</tr>
<tr id="row246823515473"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p1346883524711"><a name="p1346883524711"></a><a name="p1346883524711"></a>--bc-min-version</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 ">&nbsp;&nbsp;</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p17469123534711"><a name="p17469123534711"></a><a name="p17469123534711"></a>Outputs the lowest bytecode version supported.</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p195557124818"><a name="p195557124818"></a><a name="p195557124818"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p11894141354919"><a name="p11894141354919"></a><a name="p11894141354919"></a>-</p>
</td>
</tr>
</tbody>
</table>
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
```
<a name="table11141827153017"></a>
<table><thead align="left"><tr id="row101462717303"><th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.1"><p id="p51552743010"><a name="p51552743010"></a><a name="p51552743010"></a>Option</p>
</th>
<th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.2"><p id="p11592710304"><a name="p11592710304"></a><a name="p11592710304"></a>Description</p>
</th>
</tr>
</thead>
<tbody><tr id="row2015172763014"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p171592710306"><a name="p171592710306"></a><a name="p171592710306"></a>--dump-scopes</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p13151527133011"><a name="p13151527133011"></a><a name="p13151527133011"></a>Saves the result to a JSON file to support the debug mode in Visual Studio Code.</p>
</td>
</tr>
<tr id="row1015527173015"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p1615182712308"><a name="p1615182712308"></a><a name="p1615182712308"></a>--help</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p9556101593120"><a name="p9556101593120"></a><a name="p9556101593120"></a>Displays help information.</p>
</td>
</tr>
<tr id="row1015112763020"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p1815182733012"><a name="p1815182733012"></a><a name="p1815182733012"></a>--log-file</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1615627173019"><a name="p1615627173019"></a><a name="p1615627173019"></a>Specifies the log file output path after log printing is enabled.</p>
</td>
</tr>
<tr id="row131515277307"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p111572716304"><a name="p111572716304"></a><a name="p111572716304"></a>--optimize</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p25842312319"><a name="p25842312319"></a><a name="p25842312319"></a>Enables compilation optimization.</p>
</td>
</tr>
<tr id="row1815112753020"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p2151927193015"><a name="p2151927193015"></a><a name="p2151927193015"></a>--size-stat</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1715312588115"><a name="p1715312588115"></a><a name="p1715312588115"></a>Collects statistics on and prints ARK bytecode information after conversion.</p>
</td>
</tr>
<tr id="row1915182703012"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p17151527133017"><a name="p17151527133017"></a><a name="p17151527133017"></a>--verbose</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p15761152983113"><a name="p15761152983113"></a><a name="p15761152983113"></a>Enables log printing.</p>
</td>
</tr>
</tbody>
</table>
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
```
<a name="table125062517328"></a>
<table><thead align="left"><tr id="row125182553217"><th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.1"><p id="p175162514327"><a name="p175162514327"></a><a name="p175162514327"></a>Option</p>
</th>
<th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.2"><p id="p6512255324"><a name="p6512255324"></a><a name="p6512255324"></a>Description</p>
</th>
</tr>
</thead>
<tbody><tr id="row5511825103218"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p45172513326"><a name="p45172513326"></a><a name="p45172513326"></a>--debug</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1245695053215"><a name="p1245695053215"></a><a name="p1245695053215"></a>Enables the function for printing debug information.</p>
</td>
</tr>
<tr id="row951112515321"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p451192515323"><a name="p451192515323"></a><a name="p451192515323"></a>--debug-file</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p175142583210"><a name="p175142583210"></a><a name="p175142583210"></a>Specifies the path of the debug information output file. The default value is <strong id="b1486165094613"><a name="b1486165094613"></a><a name="b1486165094613"></a>std::cout</strong>.</p>
</td>
</tr>
<tr id="row45116253325"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p85116259328"><a name="p85116259328"></a><a name="p85116259328"></a>--help</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1348135833214"><a name="p1348135833214"></a><a name="p1348135833214"></a>Displays help information.</p>
</td>
</tr>
<tr id="row194197407327"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p154205401325"><a name="p154205401325"></a><a name="p154205401325"></a>--verbose</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p369871173312"><a name="p369871173312"></a><a name="p369871173312"></a>Outputs the comments of the output file.</p>
</td>
</tr>
</tbody>
</table>
Input file: ARK bytecodes in binary format
Output file: ARK bytecodes in text format

237
docs/工具链使用.md Normal file
View File

@ -0,0 +1,237 @@
# 工具链使用<a name="ZH-CN_TOPIC_0000001128256014"></a>
方舟前端工具采用命令行交互方式支持将JS代码转换为方舟字节码使其能够在方舟运行时上运行。支持Windows/Linux/Mac平台。
JS字节码编译工具概述
将JS文件转换为方舟字节码。
命令行格式:
```
node --expose-gc index.js [选项] file.js
```
<a name="table18706114344420"></a>
<table><thead align="left"><tr id="row19706343164411"><th class="cellrowborder" valign="top" width="15.29152915291529%" id="mcps1.1.6.1.1"><p id="p1970694310447"><a name="p1970694310447"></a><a name="p1970694310447"></a>选项</p>
</th>
<th class="cellrowborder" valign="top" width="6.830683068306829%" id="mcps1.1.6.1.2"><p id="p9548142314456"><a name="p9548142314456"></a><a name="p9548142314456"></a>缩写</p>
</th>
<th class="cellrowborder" valign="top" width="44.34443444344434%" id="mcps1.1.6.1.3"><p id="p170614318449"><a name="p170614318449"></a><a name="p170614318449"></a>描述</p>
</th>
<th class="cellrowborder" valign="top" width="26.01260126012601%" id="mcps1.1.6.1.4"><p id="p1841257144811"><a name="p1841257144811"></a><a name="p1841257144811"></a>取值范围</p>
</th>
<th class="cellrowborder" valign="top" width="7.520752075207521%" id="mcps1.1.6.1.5"><p id="p15894191313495"><a name="p15894191313495"></a><a name="p15894191313495"></a>默认值</p>
</th>
</tr>
</thead>
<tbody><tr id="row770684312444"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p0706154312447"><a name="p0706154312447"></a><a name="p0706154312447"></a>--modules</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p1654810236457"><a name="p1654810236457"></a><a name="p1654810236457"></a>-m</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p12451427144913"><a name="p12451427144913"></a><a name="p12451427144913"></a>按照Module方式编译</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p6415710488"><a name="p6415710488"></a><a name="p6415710488"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p389411314490"><a name="p389411314490"></a><a name="p389411314490"></a>-</p>
</td>
</tr>
<tr id="row8707114315446"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p7707134344416"><a name="p7707134344416"></a><a name="p7707134344416"></a>--debug-log</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p15481123194512"><a name="p15481123194512"></a><a name="p15481123194512"></a>-l</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p1988112617501"><a name="p1988112617501"></a><a name="p1988112617501"></a>使能log信息</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p34135724819"><a name="p34135724819"></a><a name="p34135724819"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p16894813204919"><a name="p16894813204919"></a><a name="p16894813204919"></a>-</p>
</td>
</tr>
<tr id="row1770734394411"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p11549171604510"><a name="p11549171604510"></a><a name="p11549171604510"></a>--dump-assembly</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p1654815237456"><a name="p1654815237456"></a><a name="p1654815237456"></a>-a</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p1024452794916"><a name="p1024452794916"></a><a name="p1024452794916"></a>输出为可读文本格式的字节码文件</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p184145774817"><a name="p184145774817"></a><a name="p184145774817"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p20894101319494"><a name="p20894101319494"></a><a name="p20894101319494"></a>-</p>
</td>
</tr>
<tr id="row17707643124417"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p1270714432449"><a name="p1270714432449"></a><a name="p1270714432449"></a>--debug</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p1548172334510"><a name="p1548172334510"></a><a name="p1548172334510"></a>-d</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p32437275490"><a name="p32437275490"></a><a name="p32437275490"></a>携带debug信息</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p134185718488"><a name="p134185718488"></a><a name="p134185718488"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p5894613104916"><a name="p5894613104916"></a><a name="p5894613104916"></a>-</p>
</td>
</tr>
<tr id="row14707184314419"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p5707194311449"><a name="p5707194311449"></a><a name="p5707194311449"></a>--show-statistics</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p1954872319456"><a name="p1954872319456"></a><a name="p1954872319456"></a>-s</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p16224102744913"><a name="p16224102744913"></a><a name="p16224102744913"></a>显示字节码相关的统计信息</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p154195704813"><a name="p154195704813"></a><a name="p154195704813"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p19894131304917"><a name="p19894131304917"></a><a name="p19894131304917"></a>-</p>
</td>
</tr>
<tr id="row768813216460"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p20688113244612"><a name="p20688113244612"></a><a name="p20688113244612"></a>--output</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p1468863218469"><a name="p1468863218469"></a><a name="p1468863218469"></a>-o</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p1468814324461"><a name="p1468814324461"></a><a name="p1468814324461"></a>输出文件路径</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p12624825135117"><a name="p12624825135117"></a><a name="p12624825135117"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p1989431344920"><a name="p1989431344920"></a><a name="p1989431344920"></a>-</p>
</td>
</tr>
<tr id="row6445636154611"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p64451436124618"><a name="p64451436124618"></a><a name="p64451436124618"></a>--timeout</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p1445113611468"><a name="p1445113611468"></a><a name="p1445113611468"></a>-t</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p1044510362466"><a name="p1044510362466"></a><a name="p1044510362466"></a>超时门限</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p745572486"><a name="p745572486"></a><a name="p745572486"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p7894111310494"><a name="p7894111310494"></a><a name="p7894111310494"></a>-</p>
</td>
</tr>
<tr id="row1978841614720"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p0788141614716"><a name="p0788141614716"></a><a name="p0788141614716"></a>--help</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p207884169473"><a name="p207884169473"></a><a name="p207884169473"></a>-h</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p178821634716"><a name="p178821634716"></a><a name="p178821634716"></a>帮助提示</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p1341757114819"><a name="p1341757114819"></a><a name="p1341757114819"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p208946134499"><a name="p208946134499"></a><a name="p208946134499"></a>-</p>
</td>
</tr>
<tr id="row14354103234714"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p1235410329479"><a name="p1235410329479"></a><a name="p1235410329479"></a>--bc-version</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 "><p id="p63541832124712"><a name="p63541832124712"></a><a name="p63541832124712"></a>-v</p>
</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p14354832134715"><a name="p14354832134715"></a><a name="p14354832134715"></a>输出当前字节码版本</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p16410577487"><a name="p16410577487"></a><a name="p16410577487"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p14894201364911"><a name="p14894201364911"></a><a name="p14894201364911"></a>-</p>
</td>
</tr>
<tr id="row246823515473"><td class="cellrowborder" valign="top" width="15.29152915291529%" headers="mcps1.1.6.1.1 "><p id="p1346883524711"><a name="p1346883524711"></a><a name="p1346883524711"></a>--bc-min-version</p>
</td>
<td class="cellrowborder" valign="top" width="6.830683068306829%" headers="mcps1.1.6.1.2 ">&nbsp;&nbsp;</td>
<td class="cellrowborder" valign="top" width="44.34443444344434%" headers="mcps1.1.6.1.3 "><p id="p17469123534711"><a name="p17469123534711"></a><a name="p17469123534711"></a>输出支持的最低字节码版本</p>
</td>
<td class="cellrowborder" valign="top" width="26.01260126012601%" headers="mcps1.1.6.1.4 "><p id="p195557124818"><a name="p195557124818"></a><a name="p195557124818"></a>-</p>
</td>
<td class="cellrowborder" valign="top" width="7.520752075207521%" headers="mcps1.1.6.1.5 "><p id="p11894141354919"><a name="p11894141354919"></a><a name="p11894141354919"></a>-</p>
</td>
</tr>
</tbody>
</table>
汇编器工具概述
工具名称为ark\_asm用于将文本格式的方舟字节码文件转换为二进制格式的方舟字节码文件。
命令行格式:
```
ark_asm [选项] 输入文件 输出文件
```
<a name="table11141827153017"></a>
<table><thead align="left"><tr id="row101462717303"><th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.1"><p id="p51552743010"><a name="p51552743010"></a><a name="p51552743010"></a>选项</p>
</th>
<th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.2"><p id="p11592710304"><a name="p11592710304"></a><a name="p11592710304"></a>描述</p>
</th>
</tr>
</thead>
<tbody><tr id="row2015172763014"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p171592710306"><a name="p171592710306"></a><a name="p171592710306"></a>--dump-scopes</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p13151527133011"><a name="p13151527133011"></a><a name="p13151527133011"></a>将结果保存到json文件中以支持在VS Code中的debug模式</p>
</td>
</tr>
<tr id="row1015527173015"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p1615182712308"><a name="p1615182712308"></a><a name="p1615182712308"></a>--help</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p9556101593120"><a name="p9556101593120"></a><a name="p9556101593120"></a>帮助提示</p>
</td>
</tr>
<tr id="row1015112763020"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p1815182733012"><a name="p1815182733012"></a><a name="p1815182733012"></a>--log-file</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1615627173019"><a name="p1615627173019"></a><a name="p1615627173019"></a>使能log打印后指定log文件输出路径</p>
</td>
</tr>
<tr id="row131515277307"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p111572716304"><a name="p111572716304"></a><a name="p111572716304"></a>--optimize</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p25842312319"><a name="p25842312319"></a><a name="p25842312319"></a>使能编译优化</p>
</td>
</tr>
<tr id="row1815112753020"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p2151927193015"><a name="p2151927193015"></a><a name="p2151927193015"></a>--size-stat</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1715312588115"><a name="p1715312588115"></a><a name="p1715312588115"></a>统计并打印出转换后方舟字节码信息</p>
</td>
</tr>
<tr id="row1915182703012"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p17151527133017"><a name="p17151527133017"></a><a name="p17151527133017"></a>--verbose</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p15761152983113"><a name="p15761152983113"></a><a name="p15761152983113"></a>使能log打印</p>
</td>
</tr>
</tbody>
</table>
输入文件:文本格式的方舟字节码
输出文件:二进制格式的方舟字节码
反汇编器工具概述
工具名称为ark\_dissam用于将二进制格式的方舟字节码文件转换为文本格式的方舟字节码文件。
命令行格式:
```
ark_dissam [选项] 输入文件 输出文件
```
<a name="table125062517328"></a>
<table><thead align="left"><tr id="row125182553217"><th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.1"><p id="p175162514327"><a name="p175162514327"></a><a name="p175162514327"></a>选项</p>
</th>
<th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.2"><p id="p6512255324"><a name="p6512255324"></a><a name="p6512255324"></a>描述</p>
</th>
</tr>
</thead>
<tbody><tr id="row5511825103218"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p45172513326"><a name="p45172513326"></a><a name="p45172513326"></a>--debug</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1245695053215"><a name="p1245695053215"></a><a name="p1245695053215"></a>使能调试信息</p>
</td>
</tr>
<tr id="row951112515321"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p451192515323"><a name="p451192515323"></a><a name="p451192515323"></a>--debug-file</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p175142583210"><a name="p175142583210"></a><a name="p175142583210"></a>调试信息输出文件路径默认为std::cout</p>
</td>
</tr>
<tr id="row45116253325"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p85116259328"><a name="p85116259328"></a><a name="p85116259328"></a>--help</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1348135833214"><a name="p1348135833214"></a><a name="p1348135833214"></a>帮助提示</p>
</td>
</tr>
<tr id="row194197407327"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p154205401325"><a name="p154205401325"></a><a name="p154205401325"></a>--verbose</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p369871173312"><a name="p369871173312"></a><a name="p369871173312"></a>增加输出文件的注释信息</p>
</td>
</tr>
</tbody>
</table>
输入文件:二进制格式的方舟字节码
输出文件:文本格式的方舟字节码

299
docs/开发实例.md Normal file
View File

@ -0,0 +1,299 @@
# 开发实例<a name="ZH-CN_TOPIC_0000001128096218"></a>
- [HelloWorld](#section105987593810)
- [运行Test262测试用例](#section118471435115815)
本章节将介绍基于方舟运行时的开发测试实例。
## HelloWorld<a name="section105987593810"></a>
运行前准备
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 <external>
.record _ESModuleMode {
u8 isModule
}
# ====================
# METHODS
.function any func_main_0_any_any_any_any_(any a0, any a1, any a2) <static> {
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测试用例<a name="section118471435115815"></a>
运行前准备
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。
<a name="table11141827153017"></a>
<table><thead align="left"><tr id="row101462717303"><th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.1"><p id="p51552743010"><a name="p51552743010"></a><a name="p51552743010"></a>选项</p>
</th>
<th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.2"><p id="p11592710304"><a name="p11592710304"></a><a name="p11592710304"></a>描述</p>
</th>
</tr>
</thead>
<tbody><tr id="row2015172763014"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p171592710306"><a name="p171592710306"></a><a name="p171592710306"></a>--h--help</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p13151527133011"><a name="p13151527133011"></a><a name="p13151527133011"></a>帮助提示</p>
</td>
</tr>
<tr id="row1015527173015"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p1615182712308"><a name="p1615182712308"></a><a name="p1615182712308"></a>--dir DIR</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p9556101593120"><a name="p9556101593120"></a><a name="p9556101593120"></a>选定要测试的目录</p>
</td>
</tr>
<tr id="row1015112763020"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p1815182733012"><a name="p1815182733012"></a><a name="p1815182733012"></a>--file FILE</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1615627173019"><a name="p1615627173019"></a><a name="p1615627173019"></a>选定要测试的文件</p>
</td>
</tr>
<tr id="row131515277307"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p111572716304"><a name="p111572716304"></a><a name="p111572716304"></a>--mode [{1, 2, 3}]</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1655718513105"><a name="p1655718513105"></a><a name="p1655718513105"></a>模式选择1仅默认值2仅严格模式3默认模式和严格模式</p>
</td>
</tr>
<tr id="row1815112753020"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p2151927193015"><a name="p2151927193015"></a><a name="p2151927193015"></a>--es51</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1715312588115"><a name="p1715312588115"></a><a name="p1715312588115"></a>运行Test262 ES5.1版本</p>
</td>
</tr>
<tr id="row1915182703012"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p17151527133017"><a name="p17151527133017"></a><a name="p17151527133017"></a>--es2015 [{all, only}]</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p15761152983113"><a name="p15761152983113"></a><a name="p15761152983113"></a>运行Test262 ES2015版本all包含的所有用例only仅包括ES2015</p>
</td>
</tr>
<tr id="row10924204611109"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p18924846111013"><a name="p18924846111013"></a><a name="p18924846111013"></a>--esnext</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p15495042191410"><a name="p15495042191410"></a><a name="p15495042191410"></a>运行Test262-ES.next</p>
</td>
</tr>
<tr id="row5161145010105"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p716125071020"><a name="p716125071020"></a><a name="p716125071020"></a>--engine FILE</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p121612050181014"><a name="p121612050181014"></a><a name="p121612050181014"></a>运行测试的其他引擎,指定二进制文件(如:d8,hermes,jsc,qjs...</p>
</td>
</tr>
<tr id="row1325585931120"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p112561595112"><a name="p112561595112"></a><a name="p112561595112"></a>--babel</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p32561959111112"><a name="p32561959111112"></a><a name="p32561959111112"></a>是否使用Babel转换</p>
</td>
</tr>
<tr id="row95230818126"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p12523158191210"><a name="p12523158191210"></a><a name="p12523158191210"></a>--timeout TIMEOUT</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p65233871210"><a name="p65233871210"></a><a name="p65233871210"></a>设置测试超时时间(以毫秒为单位)</p>
</td>
</tr>
<tr id="row474911612120"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p1274912166123"><a name="p1274912166123"></a><a name="p1274912166123"></a>--threads THREADS</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p4749121631210"><a name="p4749121631210"></a><a name="p4749121631210"></a>设置并行运行线程数</p>
</td>
</tr>
<tr id="row561512363122"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p26152036191218"><a name="p26152036191218"></a><a name="p26152036191218"></a>--hostArgs HOSTARGS</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p156151636161215"><a name="p156151636161215"></a><a name="p156151636161215"></a>传递给eshost主机的命令行参数</p>
</td>
</tr>
<tr id="row77091648111210"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p18709164871213"><a name="p18709164871213"></a><a name="p18709164871213"></a>--ark-tool ARK_TOOL</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p16709194812126"><a name="p16709194812126"></a><a name="p16709194812126"></a>方舟运行时的二进制工具</p>
</td>
</tr>
<tr id="row3767145231210"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p3767155201216"><a name="p3767155201216"></a><a name="p3767155201216"></a>--ark-frontend-tool ARK_FRONTEND_TOOL</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p4767195251220"><a name="p4767195251220"></a><a name="p4767195251220"></a>方舟前端转换工具</p>
</td>
</tr>
<tr id="row753817001311"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p553870111318"><a name="p553870111318"></a><a name="p553870111318"></a>--libs-dir LIBS_DIR</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p35384041313"><a name="p35384041313"></a><a name="p35384041313"></a>依赖so的路径集合通过“:”分割</p>
</td>
</tr>
<tr id="row08504716135"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="p11851747161314"><a name="p11851747161314"></a><a name="p11851747161314"></a>--ark-frontend [{ts2panda, es2panda}]</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="p1085144712137"><a name="p1085144712137"></a><a name="p1085144712137"></a>指定前端</p>
</td>
</tr>
</tbody>
</table>
测试运行示例
- 运行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
```

View File

@ -0,0 +1,7 @@
# 方舟运行时使用指南
- [综述](综述.md)
- [环境搭建和编译](环境搭建和编译.md)
- [开发实例](开发实例.md)
- [工具链使用](工具链使用.md)

View File

@ -0,0 +1,67 @@
# 方舟运行时子系统<a name="ZH-CN_TOPIC_0000001138852894"></a>
- [简介](#section11660541593)
- [目录](#section161941989596)
- [说明](#section18393638195820)
- [相关仓](#section1371113476307)
## 简介<a name="section11660541593"></a>
方舟\(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++语言外部函数接口。
## 目录<a name="section161941989596"></a>
```
/ark
├── js_runtime # JS运行时组件
├── runtime_core # 运行时公共组件
└── ts2abc # 方舟编译器中JavaScript语言的前端工具
```
## 说明<a name="section18393638195820"></a>
见各组件说明
## 相关仓<a name="section1371113476307"></a>
**[方舟运行时子系统](方舟运行时子系统.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)

View File

@ -0,0 +1,41 @@
# 环境搭建和编译<a name="ZH-CN_TOPIC_0000001174215863"></a>
- [环境配置](#section922419503415)
- [代码编译](#section1166711064317)
## 环境配置<a name="section922419503415"></a>
Ubuntu版本要求18.04或20.04,详细环境搭建参考:
[搭建Ubuntu环境及编译安装包方式](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-standard-package-environment.md)
## 代码编译<a name="section1166711064317"></a>
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
```

53
docs/综述.md Normal file
View File

@ -0,0 +1,53 @@
# 综述<a name="ZH-CN_TOPIC_0000001174295771"></a>
方舟\(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目前选用了TSTypeScript作为主要开发语言之一而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并发模型的目标。

View File

@ -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<JSObject> &);
using InternalSetFunc = bool (*)(JSThread *, const JSHandle<JSObject> &, const JSHandle<JSTaggedValue> &, bool);
static AccessorData *Cast(ObjectHeader *object)
{
ASSERT(JSTaggedValue(object).IsAccessorData() || JSTaggedValue(object).IsInternalAccessor());
return static_cast<AccessorData *>(object);
}
inline bool IsInternal() const
{
return GetClass()->IsInternalAccessor();
}
inline bool HasSetter() const
{
return !GetSetter().IsUndefined();
}
JSTaggedValue CallInternalGet(JSThread *thread, const JSHandle<JSObject> &obj) const
{
ASSERT(GetGetter().IsJSNativePointer());
JSNativePointer *getter = JSNativePointer::Cast(GetGetter().GetTaggedObject());
auto getFunc = reinterpret_cast<InternalGetFunc>(getter->GetExternalPointer());
return getFunc(thread, obj);
}
bool CallInternalSet(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &value,
bool mayThrow = false) const
{
ASSERT(GetSetter().IsJSNativePointer());
JSNativePointer *setter = JSNativePointer::Cast(GetSetter().GetTaggedObject());
auto setFunc = reinterpret_cast<InternalSetFunc>(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<CompletionRecord *>(object);
}
bool IsThrow() const
{
return JSTaggedValue::SameValue(this->GetType(), JSTaggedValue(static_cast<int32_t>(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

View File

@ -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<JSTaggedValue> &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<GlobalEnv> env = ecmaVm->GetGlobalEnv();
JSHandle<JSTaggedValue> isConcatsprKey = env->GetIsConcatSpreadableSymbol();
JSHandle<JSTaggedValue> 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<JSTaggedValue> &callbackfnHandle,
const JSHandle<JSTaggedValue> &valueX, const JSHandle<JSTaggedValue> &valueY,
const JSHandle<TaggedArray> &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<JSTaggedValue> 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<JSTaggedValue> 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<int32_t>(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<JSTaggedValue> xValueHandle(JSTaggedValue::ToString(thread, valueX));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
JSHandle<JSTaggedValue> 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<JSTaggedValue> &thisHandle)
{
if (thisHandle->IsJSArray()) {
return JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength();
}
if (thisHandle->IsTypedArray()) {
return TypedArrayHelper::GetArrayLength(thread, JSHandle<JSObject>::Cast(thisHandle));
}
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> 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<JSTaggedValue> &thisHandle)
{
if (thisHandle->IsJSArray()) {
return JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength();
}
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> 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

View File

@ -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 <limits>
#include <string>
#include "ecmascript/base/builtins_base.h"
namespace panda::ecmascript::base {
class ArrayHelper {
public:
static bool IsConcatSpreadable(JSThread *thread, const JSHandle<JSTaggedValue> &obj);
static int32_t SortCompare(JSThread *thread, const JSHandle<JSTaggedValue> &callbackfnHandle,
const JSHandle<JSTaggedValue> &valueX, const JSHandle<JSTaggedValue> &valueY,
const JSHandle<TaggedArray> &arg);
static double GetLength(JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle);
static double GetArrayLength(JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle);
};
} // namespace panda::ecmascript::base
#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_ARRAY_HELPER_H

View File

@ -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<TaggedArray> BuiltinsBase::GetArgsArray(EcmaRuntimeCallInfo *msg)
{
JSThread *thread = msg->GetThread();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
array_size_t length = msg->GetArgsNumber();
JSHandle<TaggedArray> 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

View File

@ -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<TaggedArray> GetArgsArray(EcmaRuntimeCallInfo *msg);
static inline JSHandle<JSTaggedValue> GetConstructor(EcmaRuntimeCallInfo *msg)
{
return msg->GetFunction();
}
static inline JSHandle<JSTaggedValue> GetThis(EcmaRuntimeCallInfo *msg)
{
return msg->GetThis();
}
static inline JSHandle<JSTaggedValue> GetNewTarget(EcmaRuntimeCallInfo *msg)
{
return msg->GetNewTarget();
}
static inline JSHandle<JSTaggedValue> 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

View File

@ -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<JSTaggedValue> 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<JSTaggedValue> handleName = globalConst->GetHandledNameString();
JSHandle<JSTaggedValue> 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<JSTaggedValue> handleMsg = globalConst->GetHandledMessageString();
JSHandle<JSTaggedValue> 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<JSTaggedValue>::Cast(factory->GetEmptyString());
} else {
msg = JSHandle<JSTaggedValue>::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<EcmaString>::Cast(name)->GetLength() == 0) {
return msg.GetTaggedValue();
}
if (JSHandle<EcmaString>::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<EcmaString> space = factory->NewFromString(": ");
JSHandle<EcmaString> jsHandleName = JSHandle<EcmaString>::Cast(name);
JSHandle<EcmaString> jsHandleMsg = JSHandle<EcmaString>::Cast(msg);
JSHandle<EcmaString> handleNameSpace = factory->ConcatFromString(jsHandleName, space);
JSHandle<EcmaString> result = factory->ConcatFromString(handleNameSpace, jsHandleMsg);
return result.GetTaggedValue();
}
JSHandle<JSTaggedValue> ErrorHelper::GetErrorName(JSThread *thread, const JSHandle<JSTaggedValue> &name,
const ErrorType &errorType)
{
auto globalConst = thread->GlobalConstants();
if (name->IsUndefined()) {
TaggedObject *errorKey = nullptr;
switch (errorType) {
case ErrorType::RANGE_ERROR:
errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledRangeErrorString());
break;
case ErrorType::EVAL_ERROR:
errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledEvalErrorString());
break;
case ErrorType::REFERENCE_ERROR:
errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledReferenceErrorString());
break;
case ErrorType::TYPE_ERROR:
errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledTypeErrorString());
break;
case ErrorType::URI_ERROR:
errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledURIErrorString());
break;
case ErrorType::SYNTAX_ERROR:
errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledSyntaxErrorString());
break;
default:
errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledErrorString());
break;
}
return JSHandle<JSTaggedValue>(thread, JSTaggedValue(errorKey));
}
return JSHandle<JSTaggedValue>::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<JSTaggedValue> ctor = BuiltinsBase::GetConstructor(argv);
JSMutableHandle<JSTaggedValue> newTarget(BuiltinsBase::GetNewTarget(argv));
if (newTarget->IsUndefined()) {
newTarget.Update(ctor.GetTaggedValue());
}
JSHandle<JSTaggedValue> message = BuiltinsBase::GetCallArg(argv, 0);
// 2. Let O be OrdinaryCreateFromConstructor(newTarget, "%ErrorPrototype%", «[[ErrorData]]»).
JSHandle<JSObject> nativeInstanceObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(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<EcmaString> handleStr = JSTaggedValue::ToString(thread, message);
LOG(DEBUG, ECMASCRIPT) << "Ark throw error: " << utf::Mutf8AsCString(handleStr->GetDataUtf8());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> msgKey = globalConst->GetHandledMessageString();
PropertyDescriptor msgDesc(thread, JSHandle<JSTaggedValue>::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<EcmaString> handleStack = BuildEcmaStackTrace(thread);
JSHandle<JSTaggedValue> stackkey = globalConst->GetHandledStackString();
PropertyDescriptor stackDesc(thread, JSHandle<JSTaggedValue>::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<EcmaString> 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

View File

@ -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<EcmaString> BuildEcmaStackTrace(JSThread *thread);
static JSHandle<JSTaggedValue> GetErrorName(JSThread *thread, const JSHandle<JSTaggedValue> &name,
const ErrorType &errorType);
};
} // namespace panda::ecmascript::base
#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_ERROR_HELPER_H

View File

@ -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 <cstdint>
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

View File

@ -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<JSTaggedValue> 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<false>();
return JSHandle<JSTaggedValue>(thread_, result);
}
JSHandle<JSTaggedValue> JsonParser::Parse(EcmaString *str)
{
ASSERT(str != nullptr);
if (UNLIKELY(str->IsUtf16())) {
size_t len = base::utf_helper::Utf16ToUtf8Size(str->GetDataUtf16(), str->GetLength()) - 1;
CVector<uint8_t> 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<uint8_t> buf(str->GetUtf8Length());
str->CopyDataUtf8(buf.data(), str->GetUtf8Length());
Text begin = buf.data();
return Parse(begin, begin + str->GetLength());
}
template <bool inObjorArr>
JSTaggedValue JsonParser::ParseJSONText()
{
SkipStartWhiteSpace();
Tokens token = ParseToken();
switch (token) {
case Tokens::OBJECT:
return ParseObject<inObjorArr>();
case Tokens::ARRAY:
return ParseArray<inObjorArr>();
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<inObjorArr>();
case Tokens::STRING:
return ParseString<inObjorArr>();
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 <bool inObjOrArr>
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 <bool inObjorArr>
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<uint16_t> 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<uint16_t> &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 <bool inObjorArr>
JSTaggedValue JsonParser::ParseArray()
{
if (UNLIKELY(*range_ != ']' && !inObjorArr)) {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception());
}
current_++;
JSHandle<JSArray> arr = factory_->NewJSArray();
if (*current_ == ']') {
return arr.GetTaggedValue();
}
JSMutableHandle<JSTaggedValue> valueHandle(thread_, JSTaggedValue::Undefined());
uint32_t index = 0;
while (current_ <= range_) {
valueHandle.Update(ParseJSONText<true>());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
FastRuntimeStub::SetPropertyByIndex<true>(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 <bool inObjorArr>
JSTaggedValue JsonParser::ParseObject()
{
if (UNLIKELY(*range_ != '}' && !inObjorArr)) {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> proto = env_->GetObjectFunction();
JSHandle<JSObject> result = factory_->NewJSObjectByConstructor(JSHandle<JSFunction>(proto), proto);
current_++;
if (*current_ == '}') {
return result.GetTaggedValue();
}
JSMutableHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> valueHandle(thread_, JSTaggedValue::Undefined());
while (current_ <= range_) {
SkipStartWhiteSpace();
if (*current_ == '"') {
keyHandle.Update(ParseString<true>());
} 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<true>());
FastRuntimeStub::SetPropertyByValue<true>(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<JSTaggedValue> JsonParser::InternalizeJsonProperty(const JSHandle<JSObject> &holder,
const JSHandle<JSTaggedValue> &name,
const JSHandle<JSTaggedValue> &receiver)
{
JSHandle<JSTaggedValue> objHandle(holder);
JSHandle<JSTaggedValue> val = JSTaggedValue::GetProperty(thread_, objHandle, name).GetValue();
JSHandle<JSTaggedValue> lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
if (val->IsECMAObject()) {
JSHandle<JSObject> obj = JSTaggedValue::ToObject(thread_, val);
bool isArray = val->IsArray(thread_);
if (isArray) {
JSHandle<JSTaggedValue> 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<JSTaggedValue> keyUnknow(thread_, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> 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<TaggedArray> ownerNames(JSObject::EnumerableOwnNames(thread_, obj));
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
array_size_t namesLength = ownerNames->GetLength();
JSMutableHandle<JSTaggedValue> keyName(thread_, JSTaggedValue::Undefined());
for (array_size_t i = 0; i < namesLength; i++) {
keyName.Update(JSTaggedValue::GetProperty(thread_, JSHandle<JSTaggedValue>(ownerNames), i)
.GetValue()
.GetTaggedValue());
RecurseAndApply(obj, keyName, receiver);
}
}
}
// Return ? Call(receiver, holder, « name, val »).
array_size_t length = 2;
JSHandle<TaggedArray> msg = factory_->NewTaggedArray(length);
msg->Set(thread_, 0, name);
msg->Set(thread_, 1, val);
JSTaggedValue result = JSFunction::Call(thread_, receiver, objHandle, msg);
return JSHandle<JSTaggedValue>(thread_, result);
}
bool JsonParser::RecurseAndApply(const JSHandle<JSObject> &holder, const JSHandle<JSTaggedValue> &name,
const JSHandle<JSTaggedValue> &receiver)
{
JSHandle<JSTaggedValue> 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

View File

@ -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<JSTaggedValue> Parse(Text begin, Text end);
JSHandle<JSTaggedValue> Parse(EcmaString *str);
JSHandle<JSTaggedValue> InternalizeJsonProperty(const JSHandle<JSObject> &holder,
const JSHandle<JSTaggedValue> &name,
const JSHandle<JSTaggedValue> &receiver);
private:
enum class Tokens : uint8_t {
// six structural tokens
OBJECT = 0,
ARRAY,
NUMBER,
STRING,
LITERAL_TRUE,
LITERAL_FALSE,
LITERAL_NULL,
TOKEN_ILLEGAL,
};
template <bool inObjorArr = false>
JSTaggedValue ParseJSONText();
template <bool inObjOrArr = false>
JSTaggedValue ParseNumber();
template <bool inObjorArr = false>
JSTaggedValue ParseString();
template <bool inObjorArr = false>
JSTaggedValue ParseArray();
template <bool inObjorArr = false>
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<uint16_t> &vec);
bool RecurseAndApply(const JSHandle<JSObject> &holder, const JSHandle<JSTaggedValue> &name,
const JSHandle<JSTaggedValue> &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

View File

@ -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 <algorithm>
#include <iomanip>
#include <sstream>
#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<int>(*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<JSTaggedValue> JsonStringifier::Stringify(const JSHandle<JSTaggedValue> &value,
const JSHandle<JSTaggedValue> &replacer,
const JSHandle<JSTaggedValue> &gap)
{
factory_ = thread_->GetEcmaVM()->GetFactory();
handleValue_ = JSMutableHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
handleKey_ = JSMutableHandle<JSTaggedValue>(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<JSArray> arr(replacer);
len = arr->GetArrayLength();
} else {
// Let len be ToLength(Get(replacer, "length")).
JSHandle<JSTaggedValue> lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> 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<JSTaggedValue> propHandle(thread_, JSTaggedValue::Undefined());
// Repeat while k<len.
for (array_size_t i = 0; i < len; i++) {
// a. Let v be Get(replacer, ToString(k)).
JSTaggedValue prop = FastRuntimeStub::FastGetPropertyByIndex(thread_, replacer.GetTaggedValue(), i);
// b. ReturnIfAbrupt(v).
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
/*
* c. Let item be undefined.
* d. If Type(v) is String, let item be v.
* e. Else if Type(v) is Number, let item be ToString(v).
* f. Else if Type(v) is Object, then
* i. If v has a [[StringData]] or [[NumberData]] internal slot, let item be ToString(v).
* ii. ReturnIfAbrupt(item).
* g. If item is not undefined and item is not currently an element of PropertyList, then
* i. Append item to the end of PropertyList.
* h. Let k be k+1.
*/
propHandle.Update(prop);
if (prop.IsNumber() || prop.IsString()) {
AddDeduplicateProp(propHandle);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
} else if (prop.IsJSPrimitiveRef()) {
JSTaggedValue primitive = JSPrimitiveRef::Cast(prop.GetTaggedObject())->GetValue();
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<EcmaString>(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<EcmaString>::Cast(gap));
}
JSHandle<JSTaggedValue> 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<JSTaggedValue>(
factory_->NewFromUtf8Literal(reinterpret_cast<const uint8_t *>(result_.c_str()), result_.size()));
}
return thread_->GlobalConstants()->GetHandledUndefined();
}
void JsonStringifier::AddDeduplicateProp(const JSHandle<JSTaggedValue> &property)
{
array_size_t propLen = propList_.size();
for (array_size_t i = 0; i < propLen; i++) {
if (JSTaggedValue::SameValue(propList_[i], property)) {
return;
}
}
JSHandle<EcmaString> primString = JSTaggedValue::ToString(thread_, property);
RETURN_IF_ABRUPT_COMPLETION(thread_);
JSHandle<JSTaggedValue> addVal(thread_, *primString);
propList_.emplace_back(addVal);
}
bool JsonStringifier::CalculateNumberGap(JSTaggedValue gap)
{
double numValue = gap.GetNumber();
int num = static_cast<int>(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<EcmaString> &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<JSTaggedValue> &object,
const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value,
const JSHandle<JSTaggedValue> &replacer)
{
JSTaggedValue tagValue = value.GetTaggedValue();
// If Type(value) is Object, then
if (value->IsECMAObject()) {
// a. Let toJSON be Get(value, "toJSON").
JSHandle<JSTaggedValue> toJson = thread_->GlobalConstants()->GetHandledToJsonString();
JSHandle<JSTaggedValue> 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<TaggedArray> 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<TaggedArray> 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<JSTaggedValue> &value,
const JSHandle<JSTaggedValue> &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<JSTaggedValue> 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<EcmaString>(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<JSTaggedValue> &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<int32_t>(key->GetInt()));
} else {
str = ConvertToString(*JSTaggedValue::ToString(thread_, key));
}
result_ += stepBegin;
str = ValueToQuotedString(str);
result_ += str;
result_ += ":";
result_ += stepEnd;
}
bool JsonStringifier::PushValue(const JSHandle<JSTaggedValue> &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<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &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<JSObject> 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<JSTaggedValue> &object, const JSHandle<JSTaggedValue> &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<JSProxy> proxy(object);
JSHandle<JSTaggedValue> lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &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<JSArray> 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<JSTaggedValue> &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<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer,
bool hasContent)
{
JSHandle<TaggedArray> 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<NumberDictionary> numberDic(elementsArr);
CVector<JSTaggedValue> 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<uint32_t>(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<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer,
bool hasContent)
{
JSHandle<TaggedArray> propertiesArr(thread_, obj->GetProperties());
if (!propertiesArr->IsDictionaryMode()) {
JSHandle<JSHClass> jsHclass(thread_, obj->GetJSHClass());
JSTaggedValue enumCache = jsHclass->GetEnumCache();
if (!enumCache.IsNull()) {
int propsNumber = jsHclass->GetPropertiesNumber();
JSHandle<TaggedArray> 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<int>(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<JSTaggedValue>(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<int>(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<JSTaggedValue>(obj));
}
handleValue_.Update(value);
hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
}
}
return hasContent;
}
JSHandle<NameDictionary> nameDic(propertiesArr);
int size = nameDic->Size();
CVector<std::pair<JSTaggedValue, PropertyAttributes>> 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<JSTaggedValue, PropertyAttributes> 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<JSTaggedValue>(obj));
}
handleValue_.Update(value);
hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
}
return hasContent;
}
bool JsonStringifier::AppendJsonString(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer,
bool hasContent)
{
JSTaggedValue serializeValue = GetSerializeValue(JSHandle<JSTaggedValue>(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

View File

@ -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<JSTaggedValue> Stringify(const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &replacer,
const JSHandle<JSTaggedValue> &gap);
private:
CString ValueToQuotedString(CString str);
bool IsFastValueToQuotedString(const char *value);
void AddDeduplicateProp(const JSHandle<JSTaggedValue> &property);
JSTaggedValue SerializeJSONProperty(const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &replacer);
JSTaggedValue GetSerializeValue(const JSHandle<JSTaggedValue> &object, const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &replacer);
void SerializeObjectKey(const JSHandle<JSTaggedValue> &key, bool hasContent);
bool SerializeJSONObject(const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &replacer);
bool SerializeJSArray(const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &replacer);
bool SerializeJSProxy(const JSHandle<JSTaggedValue> &object, const JSHandle<JSTaggedValue> &replacer);
void SerilaizePrimitiveRef(const JSHandle<JSTaggedValue> &primitiveRef);
bool PushValue(const JSHandle<JSTaggedValue> &value);
void PopValue();
bool CalculateNumberGap(JSTaggedValue gap);
bool CalculateStringGap(const JSHandle<EcmaString> &primString);
bool AppendJsonString(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer, bool hasContent);
bool SerializeElements(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer, bool hasContent);
bool SerializeKeys(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &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<JSHandle<JSTaggedValue>> stack_;
CVector<JSHandle<JSTaggedValue>> propList_;
JSMutableHandle<JSTaggedValue> handleKey_{};
JSMutableHandle<JSTaggedValue> handleValue_{};
};
} // namespace panda::ecmascript::base
#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_JSON_STRINGIFY_INL_H

View File

@ -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 <cmath>
#include <cstddef>
#include <cstdint>
#include <iomanip>
#include <sstream>
#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<uint8_t *>(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<uint64_t>(number);
value += 1;
double delta = HALF * (bit_cast<double>(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<uint8_t *>(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<int>(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<EcmaString> 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<double>(static_cast<int32_t>(d))) {
return factory->NewFromString(IntToString(static_cast<int32_t>(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, 10k1 ≤ s < 10k, the Number value for s × 10nk is m,
// and k is as small as possible. If there are multiple possibilities for s, choose the value of s for which s ×
// 10nk 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<int>(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 nk 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 kn 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 n1 is positive or negative, followed by the code units of
// the decimal representation of the integer abs(n1) (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<uint8_t *>(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<double>(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<char>(*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<uint8_t>('.')) {
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<char *>(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<uint8_t>(*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<uint64_t>(d);
int exp = static_cast<int>((u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE) - DOUBLE_EXPONENT_BIAS;
if (exp < static_cast<int>(bits - 1)) {
// smaller than INT<bits>_MAX, fast conversion
ret = static_cast<int32_t>(d);
} else if (exp < static_cast<int>(bits + DOUBLE_SIGNIFICAND_SIZE)) {
// Still has significand bits after mod 2^<bits>
// Get low <bits> 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<int32_t>(value);
if ((u64 & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK && ret != INT32_MIN) {
ret = -ret;
}
} else {
// No significand bits after mod 2^<bits>, 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

View File

@ -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 <cstdint>
#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<double>::epsilon();
static constexpr double MAX_SAFE_INTEGER = 9007199254740991;
static constexpr double MAX_VALUE = std::numeric_limits<double>::max();
static constexpr double MIN_VALUE = std::numeric_limits<double>::min();
static constexpr double POSITIVE_INFINITY = std::numeric_limits<double>::infinity();
static constexpr double NAN_VALUE = std::numeric_limits<double>::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<EcmaString> 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

View File

@ -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<UINT8_MAX> 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<char16_t *>(constChar16tData);
auto *uint16tData = reinterpret_cast<uint16_t *>(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<char16_t *>(constChar16tData);
auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
int32_t length = tmpStr.size();
return *factory->NewFromUtf16(uint16tData, length);
}
} // namespace panda::ecmascript::base

View File

@ -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 <algorithm>
#include <codecvt>
#include <locale>
#include <regex>
#include <string>
#include <vector>
#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<EcmaString> &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<const char16_t *>(utf16Data);
std::u16string u16str(char16tData, dataLen);
return u16str;
}
static inline std::u16string Utf8ToU16String(const uint8_t *utf8Data, uint32_t dataLen)
{
auto *charData = reinterpret_cast<const char *>(utf8Data);
std::string str(charData, dataLen);
std::u16string u16str = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(str);
return u16str;
}
static inline std::string WstringToString(const std::wstring &wstr)
{
return std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>{}.to_bytes(wstr);
}
static inline std::wstring StringToWstring(const std::string &str)
{
return std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>{}.from_bytes(str);
}
static inline std::string U16stringToString(const std::u16string &u16str)
{
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(u16str);
}
static inline std::u16string StringToU16string(const std::string &str)
{
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, 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<uint8_t> &data)
{
std::string str(data.begin(), data.end());
std::u32string u32str = std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t>{}.from_bytes(str);
auto u32data = reinterpret_cast<uint32_t *>(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

View File

@ -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<JSObject> &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<JSObject> &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<JSTaggedValue> &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<JSTaggedValue> TypedArrayHelper::GetConstructor(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
{
JSHandle<GlobalEnv> 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<JSFunction> TypedArrayHelper::GetConstructorFromName(JSThread *thread, const JSHandle<JSTaggedValue> &typeName)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
auto globalConst = thread->GlobalConstants();
if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt8ArrayString())) {
return JSHandle<JSFunction>(env->GetInt8ArrayFunction());
}
if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ArrayString())) {
return JSHandle<JSFunction>(env->GetUint8ArrayFunction());
}
if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ClampedArrayString())) {
return JSHandle<JSFunction>(env->GetUint8ClampedArrayFunction());
}
if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt16ArrayString())) {
return JSHandle<JSFunction>(env->GetInt16ArrayFunction());
}
if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint16ArrayString())) {
return JSHandle<JSFunction>(env->GetUint16ArrayFunction());
}
if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt32ArrayString())) {
return JSHandle<JSFunction>(env->GetInt32ArrayFunction());
}
if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint32ArrayString())) {
return JSHandle<JSFunction>(env->GetUint32ArrayFunction());
}
if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat32ArrayString())) {
return JSHandle<JSFunction>(env->GetFloat32ArrayFunction());
}
return JSHandle<JSFunction>(env->GetFloat64ArrayFunction());
}
int32_t TypedArrayHelper::GetSizeFromName(JSThread *thread, const JSHandle<JSTaggedValue> &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<JSObject> &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<JSObject> &obj,
const JSHandle<JSTaggedValue> &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<JSObject> &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<JSObject> &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<JSObject> &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<JSObject> &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<JSTaggedValue> TypedArrayHelper::GetTypedArrayName(JSThread *thread, const JSHandle<JSObject> &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<JSTaggedValue>(thread, name);
}
int32_t TypedArrayHelper::GetByteLength(JSThread *thread, const JSHandle<JSObject> &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<JSTaggedValue>(thread, length)).ToInt32();
}
int32_t TypedArrayHelper::GetByteOffset(JSThread *thread, const JSHandle<JSObject> &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<JSTaggedValue>(thread, length)).ToInt32();
}
int32_t TypedArrayHelper::GetArrayLength(JSThread *thread, const JSHandle<JSObject> &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<JSTaggedValue>(thread, length)).ToInt32();
}
} // namespace panda::ecmascript::base
#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_INL_H

View File

@ -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<JSTaggedValue> &constructorName)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
EcmaVM *ecmaVm = thread->GetEcmaVM();
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<int32_t>(index.GetNumber());
}
JSHandle<JSObject> obj =
TypedArrayHelper::AllocateTypedArray(factory, ecmaVm, constructorName, newTarget, elementLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return obj.GetTaggedValue();
}
JSHandle<JSObject> 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<JSObject> &obj)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
EcmaVM *ecmaVm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
JSHandle<JSTaggedValue> objectArg = BuiltinsBase::GetCallArg(argv, 0);
JSHandle<JSObject> object(objectArg);
// 5. Let usingIterator be ? GetMethod(object, @@iterator).
JSHandle<JSTaggedValue> iteratorSymbol = env->GetIteratorSymbol();
JSHandle<JSTaggedValue> usingIterator =
JSObject::GetMethod(thread, JSHandle<JSTaggedValue>::Cast(object), iteratorSymbol);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 6. If usingIterator is not undefined, then
if (!usingIterator->IsUndefined()) {
CVector<JSHandle<JSTaggedValue>> vec;
// a. Let values be ? IterableToList(object, usingIterator).
// b. Let len be the number of elements in values.
// c. Perform ? AllocateTypedArrayBuffer(O, len).
JSHandle<JSTaggedValue> iterator = JSIterator::GetIterator(thread, objectArg, usingIterator);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> next(thread, JSTaggedValue::True());
while (!next->IsFalse()) {
next = JSIterator::IteratorStep(thread, iterator);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!next->IsFalse()) {
JSHandle<JSTaggedValue> 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<JSTaggedValue> tKey(thread, JSTaggedValue::Undefined());
double k = 0;
while (k < len) {
tKey.Update(JSTaggedValue(k));
JSHandle<JSTaggedValue> kKey(JSTaggedValue::ToString(thread, tKey));
JSHandle<JSTaggedValue> kValue = vec[k];
JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::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<JSTaggedValue> 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<JSTaggedValue> tKey(thread, JSTaggedValue::Undefined());
double k = 0;
while (k < len) {
tKey.Update(JSTaggedValue(k));
JSHandle<JSTaggedValue> kKey(JSTaggedValue::ToString(thread, tKey));
JSHandle<JSTaggedValue> kValue = JSObject::GetProperty(thread, objectArg, kKey).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::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<JSObject> &obj,
const JSHandle<JSTaggedValue> &constructorName)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
// 5. Let srcArray be typedArray.
JSHandle<JSTaggedValue> srcArray = BuiltinsBase::GetCallArg(argv, 0);
JSHandle<JSObject> srcObj(srcArray);
// 6. Let srcData be srcArray.[[ViewedArrayBuffer]].
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> bufferConstructor =
JSObject::SpeciesConstructor(thread, JSHandle<JSObject>(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<JSTaggedValue> 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 Os [[ViewedArrayBuffer]] internal slot to data.
// 20. Set Os [[ByteLength]] internal slot to byteLength.
// 21. Set Os [[ByteOffset]] internal slot to 0.
// 22. Set Os [[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<JSObject> &obj,
const JSHandle<JSTaggedValue> &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<JSTaggedValue> byteOffset = BuiltinsBase::GetCallArg(argv, 1);
JSTaggedNumber index = JSTaggedValue::ToIndex(thread, byteOffset);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
auto offset = static_cast<int32_t>(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<JSTaggedValue> 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<int32_t>(index.GetNumber());
}
// 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
JSHandle<JSTaggedValue> 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<JSArrayBuffer>(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<JSObject> TypedArrayHelper::AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm,
const JSHandle<JSTaggedValue> &constructorName,
const JSHandle<JSTaggedValue> &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<JSObject>(thread, JSTaggedValue::Exception()));
JSHandle<JSFunction> typedArrayFunc = TypedArrayHelper::GetConstructorFromName(thread, constructorName);
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(typedArrayFunc, newTarget);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(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<JSObject> TypedArrayHelper::AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm,
const JSHandle<JSTaggedValue> &constructorName,
const JSHandle<JSTaggedValue> &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<JSObject>(thread, JSTaggedValue::Exception()));
JSHandle<JSFunction> typedArrayFunc = TypedArrayHelper::GetConstructorFromName(thread, constructorName);
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(typedArrayFunc, newTarget);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(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<JSObject>(thread, JSTaggedValue::Exception()));
// 9. Return obj.
return obj;
}
// es11 22.2.4.2.2 Runtime Semantics: AllocateTypedArrayBuffer ( O, length )
JSHandle<JSObject> TypedArrayHelper::AllocateTypedArrayBuffer(JSThread *thread, EcmaVM *ecmaVm,
const JSHandle<JSObject> &obj, double length)
{
JSHandle<GlobalEnv> 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<JSObject> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSObject> TypedArrayHelper::TypedArraySpeciesCreate(JSThread *thread, const JSHandle<JSObject> &obj,
const JSHandle<TaggedArray> &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<JSTaggedValue> defaultConstructor = TypedArrayHelper::GetConstructor(thread, JSHandle<JSTaggedValue>(obj));
// 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
JSHandle<JSTaggedValue> thisConstructor = JSObject::SpeciesConstructor(thread, obj, defaultConstructor);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(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<JSObject> TypedArrayHelper::TypedArrayCreate(JSThread *thread, const JSHandle<JSTaggedValue> &constructor,
const JSHandle<TaggedArray> &argumentList)
{
// 1. Let newTypedArray be ? Construct(constructor, argumentList).
JSTaggedValue taggedArray = JSFunction::Construct(thread, constructor, argumentList,
JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(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<JSObject>(thread, JSTaggedValue::Exception()));
}
JSHandle<JSTaggedValue> taggedArrayHandle(thread, taggedArray);
// 2. Perform ? ValidateTypedArray(newTypedArray).
TypedArrayHelper::ValidateTypedArray(thread, taggedArrayHandle);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
JSHandle<JSObject> 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<JSTaggedValue>(thread, argumentList->Get(0)))) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the length of newTypedArray is not a correct value.",
JSHandle<JSObject>(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<JSTaggedValue> &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<JSObject>::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<JSTaggedValue> &callbackfnHandle,
const JSHandle<JSTaggedValue> &buffer, const JSHandle<JSTaggedValue> &x,
const JSHandle<JSTaggedValue> &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<TaggedArray> msg = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(LEN);
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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

View File

@ -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 <limits>
#include <string>
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<JSTaggedValue> &constructorName);
static JSHandle<JSObject> AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm,
const JSHandle<JSTaggedValue> &constructorName,
const JSHandle<JSTaggedValue> &newTarget);
static JSHandle<JSObject> AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm,
const JSHandle<JSTaggedValue> &constructorName,
const JSHandle<JSTaggedValue> &newTarget, int32_t length);
static JSHandle<JSObject> TypedArraySpeciesCreate(JSThread *thread, const JSHandle<JSObject> &obj,
const JSHandle<TaggedArray> &argumentList);
static JSHandle<JSObject> TypedArrayCreate(JSThread *thread, const JSHandle<JSTaggedValue> &constructor,
const JSHandle<TaggedArray> &argumentList);
static JSTaggedValue ValidateTypedArray(JSThread *thread, const JSHandle<JSTaggedValue> &value);
inline static DataViewType GetType(const JSHandle<JSObject> &obj);
inline static int32_t GetElementSize(const JSHandle<JSObject> &obj);
inline static DataViewType GetTypeFromName(JSThread *thread, const JSHandle<JSTaggedValue> &typeName);
inline static JSHandle<JSTaggedValue> GetConstructor(JSThread *thread, const JSHandle<JSTaggedValue> &obj);
inline static JSHandle<JSFunction> GetConstructorFromName(JSThread *thread,
const JSHandle<JSTaggedValue> &typeName);
inline static int32_t GetSizeFromName(JSThread *thread, const JSHandle<JSTaggedValue> &typeName);
inline static JSTaggedValue GetViewedArrayBuffer(const JSHandle<JSObject> &obj);
inline static JSHandle<JSTaggedValue> GetTypedArrayName(JSThread *thread, const JSHandle<JSObject> &obj);
inline static int32_t GetByteLength(JSThread *thread, const JSHandle<JSObject> &obj);
inline static int32_t GetByteOffset(JSThread *thread, const JSHandle<JSObject> &obj);
inline static int32_t GetArrayLength(JSThread *thread, const JSHandle<JSObject> &obj);
static int32_t SortCompare(JSThread *thread, const JSHandle<JSTaggedValue> &callbackfnHandle,
const JSHandle<JSTaggedValue> &buffer, const JSHandle<JSTaggedValue> &x,
const JSHandle<JSTaggedValue> &y);
private:
static JSTaggedValue CreateFromOrdinaryObject(EcmaRuntimeCallInfo *argv, const JSHandle<JSObject> &obj);
static JSTaggedValue CreateFromTypedArray(EcmaRuntimeCallInfo *argv, const JSHandle<JSObject> &obj,
const JSHandle<JSTaggedValue> &constructorName);
static JSTaggedValue CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, const JSHandle<JSObject> &obj,
const JSHandle<JSTaggedValue> &constructorName);
static JSHandle<JSObject> AllocateTypedArrayBuffer(JSThread *thread, EcmaVM *ecmaVm, const JSHandle<JSObject> &obj,
double length);
inline static void SetViewedArrayBuffer(JSThread *thread, const JSHandle<JSObject> &obj,
JSTaggedValue viewedArrayBuffer);
inline static void SetTypedArrayName(JSThread *thread, const JSHandle<JSObject> &obj,
const JSHandle<JSTaggedValue> &typedArrayName);
inline static void SetByteLength(JSThread *thread, const JSHandle<JSObject> &obj, int32_t byteLength);
inline static void SetByteOffset(JSThread *thread, const JSHandle<JSObject> &obj, int32_t byteOffset);
inline static void SetArrayLength(JSThread *thread, const JSHandle<JSObject> &obj, int32_t arrayLength);
};
} // namespace panda::ecmascript::base
#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_H

View File

@ -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<int32_t>(lead) << 10UL) + static_cast<int32_t>(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<uint8_t> &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<uint8_t>(UTF8_3B_FIRST | static_cast<uint8_t>(d0 >> UtfOffset::TWELVE));
auto ch1 = static_cast<uint8_t>(UTF8_3B_SECOND | (static_cast<uint8_t>(d0 >> UtfOffset::SIX) & utf::MASK_6BIT));
auto ch2 = static_cast<uint8_t>(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<uint8_t>(d0)}};
}
if (d0 <= UTF8_2B_MAX) {
auto ch0 = static_cast<uint8_t>(UTF8_2B_FIRST | static_cast<uint8_t>(d0 >> UtfOffset::SIX));
auto ch1 = static_cast<uint8_t>(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<uint8_t>(UTF8_3B_FIRST | static_cast<uint8_t>(d0 >> UtfOffset::TWELVE));
auto ch1 = static_cast<uint8_t>(UTF8_3B_SECOND | (static_cast<uint8_t>(d0 >> UtfOffset::SIX) & utf::MASK_6BIT));
auto ch2 = static_cast<uint8_t>(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<uint8_t>((codePoint >> UtfOffset::EIGHTEEN) | UTF8_4B_FIRST);
auto ch1 = static_cast<uint8_t>(((codePoint >> UtfOffset::TWELVE) & utf::MASK_6BIT) | utf::MASK1);
auto ch2 = static_cast<uint8_t>(((codePoint >> UtfOffset::SIX) & utf::MASK_6BIT) | utf::MASK1);
auto ch3 = static_cast<uint8_t>((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<uint32_t, size_t> 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

View File

@ -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 <cstdint>
#include <vector>
#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<uint8_t, MAX_BYTES> ch;
};
uint32_t UTF16Decode(uint16_t lead, uint16_t trail);
bool IsValidUTF8(const std::vector<uint8_t> &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<uint32_t, size_t> 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

2401
ecmascript/builtins.cpp Normal file

File diff suppressed because it is too large Load Diff

201
ecmascript/builtins.h Normal file
View File

@ -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<GlobalEnv> &env, JSThread *thread);
private:
JSThread *thread_{nullptr};
ObjectFactory *factory_{nullptr};
EcmaVM *vm_{nullptr};
JSHandle<JSFunction> NewBuiltinConstructor(const JSHandle<GlobalEnv> &env, const JSHandle<JSObject> &prototype,
EcmaEntrypoint ctorFunc, const char *name, int length) const;
JSHandle<JSFunction> NewFunction(const JSHandle<GlobalEnv> &env, const JSHandle<JSTaggedValue> &key,
EcmaEntrypoint func, int length) const;
void InitializeCtor(const JSHandle<GlobalEnv> &env, const JSHandle<JSObject> &prototype,
const JSHandle<JSFunction> &ctor, const char *name, int length) const;
void InitializeGlobalObject(const JSHandle<GlobalEnv> &env, const JSHandle<JSObject> &globalObject);
void InitializeFunction(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &emptyFuncDynclass) const;
void InitializeObject(const JSHandle<GlobalEnv> &env, const JSHandle<JSObject> &objFuncPrototype,
const JSHandle<JSObject> &objFunc);
void InitializeNumber(const JSHandle<GlobalEnv> &env, const JSHandle<JSObject> &globalObject,
const JSHandle<JSHClass> &primRefObjDynclass);
void InitializeDate(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeBoolean(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &primRefObjDynclass) const;
void InitializeSymbol(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeSymbolWithRealm(const JSHandle<GlobalEnv> &realm, const JSHandle<JSHClass> &objFuncInstanceDynclass);
void InitializeArray(const JSHandle<GlobalEnv> &env, const JSHandle<JSTaggedValue> &objFuncPrototypeVal) const;
void InitializeTypedArray(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeInt8Array(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeUint8Array(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeUint8ClampedArray(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeInt16Array(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeUint16Array(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeInt32Array(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeUint32Array(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeFloat32Array(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeFloat64Array(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeAllTypeError(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeAllTypeErrorWithRealm(const JSHandle<GlobalEnv> &realm) const;
void InitializeError(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass,
const JSType &errorTag) const;
void SetErrorWithRealm(const JSHandle<GlobalEnv> &realm, const JSType &errorTag) const;
void InitializeRegExp(const JSHandle<GlobalEnv> &env);
void GeneralUpdateError(ErrorParameter *error, EcmaEntrypoint constructor, EcmaEntrypoint method, const char *name,
JSType type) const;
void InitializeSet(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeMap(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeWeakMap(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeWeakSet(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeMath(const JSHandle<GlobalEnv> &env, const JSHandle<JSTaggedValue> &objFuncPrototypeVal) const;
void InitializeJson(const JSHandle<GlobalEnv> &env, const JSHandle<JSTaggedValue> &objFuncPrototypeVal) const;
void InitializeString(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &primRefObjDynclass) const;
void InitializeIterator(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeStringIterator(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &iteratorFuncDynclass) const;
void InitializeForinIterator(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &iteratorFuncDynclass) const;
void InitializeMapIterator(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &iteratorFuncDynclass) const;
void InitializeSetIterator(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &iteratorFuncDynclass) const;
void InitializeArrayIterator(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &iteratorFuncDynclass) const;
void InitializeArrayBuffer(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeDataView(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeProxy(const JSHandle<GlobalEnv> &env);
void InitializeReflect(const JSHandle<GlobalEnv> &env, const JSHandle<JSTaggedValue> &objFuncPrototypeVal) const;
void InitializeAsyncFunction(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeGeneratorFunction(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeGenerator(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
JSHandle<JSFunction> InitializeExoticConstructor(const JSHandle<GlobalEnv> &env, EcmaEntrypoint ctorFunc,
const char *name, int length);
void InitializePromise(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &promiseFuncDynclass);
void InitializePromiseJob(const JSHandle<GlobalEnv> &env);
void SetFunction(const JSHandle<GlobalEnv> &env, const JSHandle<JSObject> &obj, const char *key,
EcmaEntrypoint func, int length) const;
void SetFunction(const JSHandle<GlobalEnv> &env, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
EcmaEntrypoint func, int length) const;
void SetFuncToObjAndGlobal(const JSHandle<GlobalEnv> &env, const JSHandle<JSObject> &globalObject,
const JSHandle<JSObject> &obj, const char *key, EcmaEntrypoint func, int length);
template <int type = JSSymbol::SYMBOL_DEFAULT_TYPE>
void SetFunctionAtSymbol(const JSHandle<GlobalEnv> &env, const JSHandle<JSObject> &obj,
const JSHandle<JSTaggedValue> &symbol, const char *name, EcmaEntrypoint func,
int length) const;
void SetStringTagSymbol(const JSHandle<GlobalEnv> &env, const JSHandle<JSObject> &obj, const char *key) const;
JSHandle<JSTaggedValue> CreateGetter(const JSHandle<GlobalEnv> &env, EcmaEntrypoint func, const char *name,
int length) const;
void SetConstant(const JSHandle<JSObject> &obj, const char *key, JSTaggedValue value) const;
void SetGlobalThis(const JSHandle<JSObject> &obj, const char *key, const JSHandle<JSTaggedValue> &globalValue);
void SetAttribute(const JSHandle<JSObject> &obj, const char *key, const char *value) const;
void SetNoneAttributeProperty(const JSHandle<JSObject> &obj, const char *key,
const JSHandle<JSTaggedValue> &value) const;
void StrictModeForbiddenAccessCallerArguments(const JSHandle<GlobalEnv> &env,
const JSHandle<JSObject> &prototype) const;
JSHandle<JSTaggedValue> CreateSetter(const JSHandle<GlobalEnv> &env, EcmaEntrypoint func, const char *name,
int length);
void SetArgumentsSharedAccessor(const JSHandle<GlobalEnv> &env);
void SetAccessor(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &getter, const JSHandle<JSTaggedValue> &setter) const;
void SetGetter(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &getter) const;
void InitializeJSNativeObject(const JSHandle<GlobalEnv> &env) const;
};
} // namespace panda::ecmascript
#endif // PANDA_RUNTIME_ECMA_INIT_H

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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 <typeinfo>
#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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSArrayBuffer> arrBuf(thisHandle);
// 5. Let length be the value of Os [[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<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
// 1. Let O be the this value.
JSHandle<JSTaggedValue> 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<JSArrayBuffer> 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 Os [[ArrayBufferByteLength]] internal slot.
JSTaggedNumber lengthNum = JSTaggedNumber::FromIntOrDouble(thread, arrBuf->GetArrayBufferByteLength());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
JSHandle<JSObject> objHandle(thisHandle);
JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
// 14. ReturnIfAbrupt(ctor).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 15. Let new be Construct(ctor, «newLen»).
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
JSHandle<TaggedArray> arguments = factory->NewTaggedArray(1);
arguments->Set(thread, 0, JSTaggedValue(newLen));
JSTaggedValue taggedNewArrBuf = JSFunction::Construct(thread, constructor, arguments, undefined);
JSHandle<JSTaggedValue> 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<JSArrayBuffer> newJsArrBuf(newArrBuf);
// 20. If the value of news [[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 Os [[ArrayBufferData]] internal slot.
JSTaggedValue from = arrBuf->GetArrayBufferData();
// 24. Let toBuf be the value of news [[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<JSTaggedValue> &newTarget,
double byteLength)
{
BUILTINS_API_TRACE(thread, ArrayBuffer, AllocateArrayBuffer);
/**
* 1. Let obj be OrdinaryCreateFromConstructor(constructor, "%ArrayBufferPrototype%",
* «[[ArrayBufferData]], [[ArrayBufferByteLength]]» ).
* */
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> arrBufFunc = env->GetArrayBufferFunction();
JSHandle<JSObject> obj;
if (!newTarget->IsBoundFunction()) {
obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(arrBufFunc), newTarget);
// 2. ReturnIfAbrupt
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
JSHandle<JSTaggedValue> prototypeKey = thread->GlobalConstants()->GetHandledPrototypeString();
JSHandle<JSTaggedValue> constructTag(newTarget);
JSHandle<JSTaggedValue> constructProto =
JSTaggedValue::GetProperty(thread, constructTag, prototypeKey).GetValue();
obj = JSObject::ObjectCreate(thread, JSHandle<JSObject>(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<JSArrayBuffer> arrayBuffer(obj);
// 6. Set objs [[ArrayBufferData]] internal slot to block.
factory->NewJSArrayBufferData(arrayBuffer, byteLength);
// 7. Set objs [[ArrayBufferByteLength]] internal slot to byteLength.
arrayBuffer->SetArrayBufferByteLength(thread, JSTaggedValue(static_cast<int32_t>(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 arrayBuffers [[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<JSTaggedValue> &srcBuffer,
int32_t srcByteOffset, JSHandle<JSTaggedValue> constructor)
{
BUILTINS_API_TRACE(thread, ArrayBuffer, CloneArrayBuffer);
// 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
ASSERT(srcBuffer->IsArrayBuffer());
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
// 2. If cloneConstructor is not present
if (constructor->IsUndefined()) {
// a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%).
JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
JSHandle<JSObject> 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 srcBuffers [[ArrayBufferByteLength]] internal slot.
JSHandle<JSArrayBuffer> 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 targetBuffers [[ArrayBufferData]] internal slot.
JSHandle<JSArrayBuffer> newArrBuf(thread, taggedBuf);
// Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength).
// 7. Let srcBlock be the value of srcBuffers [[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<uint8_t *>(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<int8_t>(res);
return GetTaggedInt(int8Res);
}
case DataViewType::UINT16:
return GetValueFromBufferForInteger<uint16_t, NumberSize::UINT16>(block, byteIndex, littleEndian);
case DataViewType::INT16:
return GetValueFromBufferForInteger<int16_t, NumberSize::INT16>(block, byteIndex, littleEndian);
case DataViewType::UINT32:
return GetValueFromBufferForInteger<uint32_t, NumberSize::UINT32>(block, byteIndex, littleEndian);
case DataViewType::INT32:
return GetValueFromBufferForInteger<int32_t, NumberSize::INT32>(block, byteIndex, littleEndian);
case DataViewType::FLOAT32:
return GetValueFromBufferForFloat<float, NumberSize::FLOAT32>(block, byteIndex, littleEndian);
case DataViewType::FLOAT64:
return GetValueFromBufferForFloat<double, NumberSize::FLOAT64>(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<uint8_t *>(pointer);
double val = value.GetNumber();
switch (type) {
case DataViewType::UINT8:
SetValueInBufferForByte<uint8_t>(val, block, byteIndex);
break;
case DataViewType::UINT8_CLAMPED:
SetValueInBufferForUint8Clamped(val, block, byteIndex);
break;
case DataViewType::INT8:
SetValueInBufferForByte<int8_t>(val, block, byteIndex);
break;
case DataViewType::UINT16:
SetValueInBufferForInteger<uint16_t>(val, block, byteIndex, littleEndian);
break;
case DataViewType::INT16:
SetValueInBufferForInteger<int16_t>(val, block, byteIndex, littleEndian);
break;
case DataViewType::UINT32:
SetValueInBufferForInteger<uint32_t>(val, block, byteIndex, littleEndian);
break;
case DataViewType::INT32:
SetValueInBufferForInteger<int32_t>(val, block, byteIndex, littleEndian);
break;
case DataViewType::FLOAT32:
SetValueInBufferForFloat<float>(val, block, byteIndex, littleEndian);
break;
case DataViewType::FLOAT64:
SetValueInBufferForFloat<double>(val, block, byteIndex, littleEndian);
break;
default:
UNREACHABLE();
}
return JSTaggedValue::Undefined();
}
template <typename T>
void BuiltinsArrayBuffer::SetTypeData(uint8_t *block, T value, int32_t index)
{
int32_t sizeCount = sizeof(T);
auto *res = reinterpret_cast<uint8_t *>(&value);
for (int i = 0; i < sizeCount; i++) {
*(block + index + i) = *(res + i); // NOLINT
}
}
template <typename T>
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 <typename T>
T BuiltinsArrayBuffer::TransformFloatToBigEndian(T fValue)
{
static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, "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 <typename T>
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<T *>(tmpArr);
T res = *arr;
return res;
}
template <typename T, BuiltinsArrayBuffer::NumberSize size>
JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, int32_t byteIndex, bool littleEndian)
{
static_assert(std::is_integral_v<T>, "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<T>(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<T, uint32_t>) {
// NOLINTNEXTLINE(clang-diagnostic-sign-compare)
if (res > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
return GetTaggedDouble(static_cast<double>(res));
}
}
return GetTaggedInt(res);
}
template <typename T, BuiltinsArrayBuffer::NumberSize size>
JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForFloat(uint8_t *block, int32_t byteIndex, bool littleEndian)
{
static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, "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<T>(block, byteIndex, tmpArr, capacity);
// NOLINTNEXTLINE(readability-braces-around-statements)
if constexpr (std::is_same_v<T, float>) {
if (std::isnan(tmp)) {
return GetTaggedDouble(tmp);
}
} else if constexpr (std::is_same_v<T, double>) { // 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 <typename T>
void BuiltinsArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, int32_t byteIndex)
{
static_assert(std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>, "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<int64_t>(val);
auto *resArr = reinterpret_cast<T *>(&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<int64_t>(val);
SetTypeData(block, res, byteIndex);
}
template <typename T>
void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, int32_t byteIndex, bool littleEndian)
{
static_assert(std::is_integral_v<T>, "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<int64_t>(val);
// NOLINTNEXTLINE(readability-braces-around-statements)
if constexpr (std::is_same_v<T, uint16_t>) {
auto *pTmp = reinterpret_cast<int16_t *>(&int64Val);
int16_t tmp = *pTmp;
res = static_cast<T>(tmp);
} else { // NOLINTNEXTLINE(readability-braces-around-statements)
auto *pTmp = reinterpret_cast<T *>(&int64Val);
res = *pTmp;
}
if (!littleEndian) {
res = TransformIntToBigEndian<T>(res);
}
SetTypeData(block, res, byteIndex);
}
template <typename T>
void BuiltinsArrayBuffer::SetValueInBufferForFloat(double val, uint8_t *block, int32_t byteIndex, bool littleEndian)
{
static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, "T must be float type");
auto data = static_cast<T>(val);
if (std::isnan(val)) {
SetTypeData(block, data, byteIndex);
return;
}
if (!littleEndian) {
auto res = TransformFloatToBigEndian<T>(data);
SetTypeData(block, res, byteIndex);
return;
}
SetTypeData(block, data, byteIndex);
}
} // namespace panda::ecmascript::builtins

View File

@ -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<JSTaggedValue> &srcBuffer,
int32_t srcByteOffset, JSHandle<JSTaggedValue> constructor);
// 24.1.1.1 AllocateArrayBuffer(constructor, byteLength)
static JSTaggedValue AllocateArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &newTarget,
double byteLength);
private:
template <typename T>
static T TransformIntToBigEndian(T liValue);
template <typename T>
static T TransformFloatToBigEndian(T fValue);
template <typename T>
static T TransformByteToNumber(uint8_t *block, int32_t index, uint8_t *tmpArr, int32_t size);
template <typename T>
static void SetTypeData(uint8_t *block, T value, int32_t index);
template <typename T, NumberSize size>
static JSTaggedValue GetValueFromBufferForInteger(uint8_t *block, int32_t byteIndex, bool littleEndian);
template <typename T, NumberSize size>
static JSTaggedValue GetValueFromBufferForFloat(uint8_t *block, int32_t byteIndex, bool littleEndian);
template <typename T>
static void SetValueInBufferForByte(double val, uint8_t *block, int32_t byteIndex);
static void SetValueInBufferForUint8Clamped(double val, uint8_t *block, int32_t byteIndex);
template <typename T>
static void SetValueInBufferForInteger(double val, uint8_t *block, int32_t byteIndex, bool littleEndian);
template <typename T>
static void SetValueInBufferForFloat(double val, uint8_t *block, int32_t byteIndex, bool littleEndian);
};
} // namespace panda::ecmascript::builtins
#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_ARRAYBUFFER_H

View File

@ -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

View File

@ -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

View File

@ -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<JSTaggedValue> 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<JSFunction> ctor = JSHandle<JSFunction>(GetConstructor(argv));
JSHandle<JSObject> 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

View File

@ -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

View File

@ -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<JSTaggedValue> ctor = GetConstructor(argv);
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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 buffers [[ArrayBufferByteLength]] internal slot.
JSHandle<JSArrayBuffer> 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<JSTaggedValue> 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<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), newTarget);
// 14. ReturnIfAbrupt(O).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSDataView> dataView(obj);
// 15. Set Os [[DataView]] internal slot to true.
dataView->SetDataView(thread, JSTaggedValue::True());
// 16. Set Os [[ViewedArrayBuffer]] internal slot to buffer.
dataView->SetViewedArrayBuffer(thread, bufferHandle.GetTaggedValue());
// 17. Set Os [[ByteLength]] internal slot to viewByteLength.
dataView->SetByteLength(thread, JSTaggedValue(viewByteLen));
// 18. Set Os [[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<JSTaggedValue> 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<JSDataView> dataView(thisHandle);
// 4. Let buffer be the value of Os [[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<JSTaggedValue> 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<JSDataView> dataView(thisHandle);
// 4. Let buffer be the value of Os [[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 Os [[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<JSTaggedValue> 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<JSDataView> dataView(thisHandle);
// 4. Let buffer be the value of Os [[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 Os [[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<JSTaggedValue> &view,
const JSHandle<JSTaggedValue> &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 views [[ViewedArrayBuffer]] internal slot.
JSHandle<JSDataView> 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 views [[ByteOffset]] internal slot.
JSTaggedNumber offsetNum = JSTaggedNumber::FromIntOrDouble(thread, dataView->GetByteOffset());
int32_t offset = offsetNum.ToInt32();
// 11. Let viewSize be the value of views [[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<JSTaggedValue> &view,
const JSHandle<JSTaggedValue> &requestIndex, JSTaggedValue littleEndian,
DataViewType type, const JSHandle<JSTaggedValue> &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 views [[ViewedArrayBuffer]] internal slot.
JSHandle<JSDataView> 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 views [[ByteOffset]] internal slot.
JSTaggedNumber offsetNum = JSTaggedNumber::FromIntOrDouble(thread, dataView->GetByteOffset());
int32_t offset = offsetNum.ToInt32();
// 11. Let viewSize be the value of views [[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<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSTaggedValue> offsetHandle = GetCallArg(argv, 0);
if (type == DataViewType::UINT8 || type == DataViewType::INT8) {
return GetViewValue(thread, thisHandle, offsetHandle, JSTaggedValue::True(), type);
}
JSHandle<JSTaggedValue> 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<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSTaggedValue> offsetHandle = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 1);
if (type == DataViewType::UINT8 || type == DataViewType::INT8) {
return SetViewValue(thread, thisHandle, offsetHandle, JSTaggedValue::True(), type, value);
}
JSHandle<JSTaggedValue> littleEndianHandle = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
return SetViewValue(thread, thisHandle, offsetHandle, littleEndianHandle.GetTaggedValue(), type, value);
}
} // namespace panda::ecmascript::builtins

View File

@ -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<JSTaggedValue> &view,
const JSHandle<JSTaggedValue> &requestIndex, JSTaggedValue littleEndian,
DataViewType type);
static JSTaggedValue SetViewValue(JSThread *thread, const JSHandle<JSTaggedValue> &view,
const JSHandle<JSTaggedValue> &requestIndex, JSTaggedValue littleEndian,
DataViewType type, const JSHandle<JSTaggedValue> &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

View File

@ -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<JSTaggedValue> 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<JSTaggedValue> value = GetCallArg(argv, 0);
if (value->IsDate()) { // The value is a date object.
JSHandle<JSDate> jsDate(thread, JSDate::Cast(value->GetTaggedObject()));
timeValue = jsDate->GetTimeValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
JSHandle<JSTaggedValue> 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<int64_t, DATE_LENGTH> 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<JSTaggedValue> 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<int64_t>(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<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSDate> dateObject =
JSHandle<JSDate>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
dateObject->SetTimeValue(thread, timeValue);
return JSTaggedValue(JSObject::Cast(static_cast<TaggedObject *>(*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<JSTaggedValue> 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<JSTaggedValue> msg = GetThis(argv);
if (!msg->IsDate()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception());
}
JSHandle<JSDate> 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<JSTaggedValue> msg = GetThis(argv);
JSHandle<JSObject> object = JSTaggedValue::ToObject(thread, msg);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 2. Let tv be ToPrimitive(hint Number)
JSHandle<JSTaggedValue> objectHandle = JSHandle<JSTaggedValue>::Cast(object);
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> object = GetThis(argv);
if (!object->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Not a JSObject", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> hint = GetCallArg(argv, 0);
PreferredPrimitiveType tryFirst = PREFER_STRING;
if (hint->IsString()) {
JSHandle<EcmaString> numberStrHandle = factory->NewFromString("number");
if (EcmaString::StringsAreEqual(hint.GetObject<EcmaString>(), *numberStrHandle)) {
tryFirst = PREFER_NUMBER;
} else {
JSHandle<EcmaString> stringStrHandle = factory->NewFromString("string");
JSHandle<EcmaString> defaultStrHandle = factory->NewFromString("default");
if (EcmaString::StringsAreEqual(hint.GetObject<EcmaString>(), *stringStrHandle) ||
EcmaString::StringsAreEqual(hint.GetObject<EcmaString>(), *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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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<JSTaggedValue> func = GetThis(argv);
JSHandle<JSTaggedValue> 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<TaggedArray> emptyArray = factory->NewTaggedArray(0);
return JSFunction::Call(thread, func, thisArg, emptyArray);
}
// 3. Let argList be CreateListFromArrayLike(argArray).
JSHandle<JSTaggedValue> arrayObj = GetCallArg(argv, 1);
JSHandle<TaggedArray> argList = JSHandle<TaggedArray>::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<JSTaggedValue> 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<JSTaggedValue> 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<TaggedArray> 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<JSFunctionBase> targetFunction = JSHandle<JSFunctionBase>::Cast(target);
JSHandle<JSBoundFunction> 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<JSTaggedValue> lengthKey = globalConst->GetHandledLengthString();
bool targetHasLength =
JSTaggedValue::HasOwnProperty(thread, JSHandle<JSTaggedValue>::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<JSTaggedValue> 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<double>(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<JSTaggedValue>(thread, JSTaggedValue(lengthValue)), false, false, true);
[[maybe_unused]] bool status =
JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(boundFunction), lengthKey, desc);
// 11. Assert: status is not an abrupt completion.
ASSERT_PRINT(status, "DefinePropertyOrThrow failed");
// 12. Let targetName be Get(Target, "name").
JSHandle<JSTaggedValue> nameKey = globalConst->GetHandledNameString();
JSHandle<JSTaggedValue> targetName = JSObject::GetProperty(thread, target, nameKey).GetValue();
// 13. ReturnIfAbrupt(targetName).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> 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<JSTaggedValue> emptyString(factory->GetEmptyString());
status = JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(boundFunction), emptyString, boundName);
} else {
status = JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(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<JSTaggedValue> func = GetThis(argv);
JSHandle<JSTaggedValue> 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<TaggedArray> 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<JSTaggedValue> 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<JSTaggedValue> thisValue = GetThis(argv);
// 2. Return OrdinaryHasInstance(F, V).
JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0);
return JSFunction::OrdinaryHasInstance(argv->GetThread(), thisValue, arg) ? GetTaggedBoolean(true)
: GetTaggedBoolean(false);
}
} // namespace panda::ecmascript::builtins

View File

@ -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

View File

@ -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<JSTaggedValue> msg = GetThis(argv);
if (!msg->IsGeneratorObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Not a generator object.", JSTaggedValue::Exception());
}
JSHandle<JSGeneratorObject> generator(thread, JSGeneratorObject::Cast(*JSTaggedValue::ToObject(thread, msg)));
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
// 2.Return ? GeneratorResume(g, value).
JSHandle<JSObject> 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<JSTaggedValue> msg = GetThis(argv);
if (!msg->IsGeneratorObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Not a generator object.", JSTaggedValue::Exception());
}
JSHandle<JSGeneratorObject> generator(thread, JSGeneratorObject::Cast(*JSTaggedValue::ToObject(thread, msg)));
// 2.Let C be Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<CompletionRecord> completionRecord =
factory->NewCompletionRecord(CompletionRecord::RETURN, value);
// 3.Return ? GeneratorResumeAbrupt(g, C).
JSHandle<JSObject> 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<JSTaggedValue> msg = GetThis(argv);
if (!msg->IsGeneratorObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Not a generator object.", JSTaggedValue::Exception());
}
JSHandle<JSGeneratorObject> generator(thread, JSGeneratorObject::Cast(*JSTaggedValue::ToObject(thread, msg)));
// 2.Let C be ThrowCompletion(exception).
JSHandle<JSTaggedValue> exception = GetCallArg(argv, 0);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<CompletionRecord> completionRecord =
factory->NewCompletionRecord(CompletionRecord::THROW, exception);
// 3.Return ? GeneratorResumeAbrupt(g, C).
JSHandle<JSObject> result = JSGeneratorObject::GeneratorResumeAbrupt(thread, generator, completionRecord);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return result.GetTaggedValue();
}
} // namespace panda::ecmascript::builtins

View File

@ -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

View File

@ -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 <random>
#include <sstream>
#include <string>
#include <vector>
#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<JSTaggedValue> 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<JSTaggedValue> 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<EcmaString> 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<EcmaString> 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<EcmaString> 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<EcmaString> 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<EcmaString> &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<uint16_t *>(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<EcmaString> &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<uint16_t *>(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<EcmaString> 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<uint16_t *>(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<uint8_t> 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<uint16_t *>(&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<EcmaString> 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<JSBoundFunction> boundFunc(GetConstructor(msg));
JSHandle<JSTaggedValue> thisObj(thread, boundFunc->GetBoundThis());
JSHandle<TaggedArray> 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<JSProxy> 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<JSTaggedValue> thisObj(GetThis(msg));
JSHandle<TaggedArray> 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

View File

@ -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<EcmaString> &str, judgURIFunc IsInURISet);
static JSTaggedValue Decode(JSThread *thread, const JSHandle<EcmaString> &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

View File

@ -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<JSTaggedValue> value = GetCallArg(argv, 0);
JSHandle<JSObject> iterResult = JSIterator::CreateIterResultObject(thread, value, true);
return iterResult.GetTaggedValue();
}
JSTaggedValue BuiltinsIterator::GetIteratorObj(EcmaRuntimeCallInfo *argv)
{
return base::BuiltinsBase::GetThis(argv).GetTaggedValue();
}
} // namespace panda::ecmascript::builtins

View File

@ -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

View File

@ -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<JSObject> syntaxError = factory->GetJSError(base::ErrorType::SYNTAX_ERROR, "arg is empty");
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
JSHandle<EcmaString> parseString = JSTaggedValue::ToString(thread, msg);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
panda::ecmascript::base::JsonParser parser(thread);
JSHandle<JSTaggedValue> 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<JSTaggedValue> callbackfnHandle(thread, reviver);
// Let root be ! OrdinaryObjectCreate(%Object.prototype%).
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> constructor = env->GetObjectFunction();
JSHandle<JSObject> root = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor);
// Let rootName be the empty String.
JSHandle<JSTaggedValue> 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<JSTaggedValue> handleValue(thread, value);
JSHandle<JSTaggedValue> handleReplacer(thread, replacer);
JSHandle<JSTaggedValue> handleGap(thread, gap);
panda::ecmascript::base::JsonStringifier stringifier(thread);
JSHandle<JSTaggedValue> result = stringifier.Stringify(handleValue, handleReplacer, handleGap);
return result.GetTaggedValue();
}
} // namespace panda::ecmascript::builtins

View File

@ -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

View File

@ -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<JSTaggedValue> 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<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
// 3.returnIfAbrupt()
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSMap> map = JSHandle<JSMap>::Cast(obj);
// 4.Set maps [[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<JSTaggedValue> 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<JSTaggedValue> adderKey(factory->NewFromString("set"));
JSHandle<JSTaggedValue> adder = JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(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<JSTaggedValue> iter(JSIterator::GetIterator(thread, iterable));
// ReturnIfAbrupt(iter).
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue());
JSHandle<JSTaggedValue> keyIndex(thread, JSTaggedValue(0));
JSHandle<JSTaggedValue> valueIndex(thread, JSTaggedValue(1));
JSHandle<JSTaggedValue> next = JSIterator::IteratorStep(thread, iter);
JSMutableHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSObject> typeError = factory->GetJSError(ErrorType::TYPE_ERROR, "nextItem is not Object");
JSHandle<JSTaggedValue> record(
factory->NewCompletionRecord(CompletionRecord::THROW, JSHandle<JSTaggedValue>(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<JSTaggedValue> 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<TaggedArray> array(factory->NewTaggedArray(2)); // 2: key and value pair
array->Set(thread, 0, key);
// Let v be Get(nextItem, "1").
JSHandle<JSTaggedValue> 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<JSTaggedValue>(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<JSTaggedValue> 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<JSTaggedValue> key = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 1);
JSHandle<JSMap> 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<JSTaggedValue> 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<JSMap> 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<JSTaggedValue> 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<JSMap> map(self);
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSMap> map(thread, JSMap::Cast(*JSTaggedValue::ToObject(thread, self)));
// 4.If IsCallable(callbackfn) is false, throw a TypeError exception.
JSHandle<JSTaggedValue> 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<JSTaggedValue> thisArg = GetCallArg(argv, 1);
// composed arguments
int arguementsLength = 3;
JSHandle<TaggedArray> array(factory->NewTaggedArray(arguementsLength));
JSHandle<JSTaggedValue> iter(factory->NewJSMapIterator(map, IterationKind::KEY_AND_VALUE));
JSHandle<JSTaggedValue> keyIndex(thread, JSTaggedValue(0));
JSHandle<JSTaggedValue> valueIndex(thread, JSTaggedValue(1));
JSHandle<JSTaggedValue> result = JSIterator::IteratorStep(thread, iter);
while (!result->IsFalse()) {
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result.GetTaggedValue());
JSHandle<JSTaggedValue> iterValue(JSIterator::IteratorValue(thread, result));
JSHandle<JSTaggedValue> key = JSObject::GetProperty(thread, iterValue, keyIndex).GetValue();
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> self = GetThis(argv);
JSHandle<JSTaggedValue> 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<JSTaggedValue> self = GetThis(argv);
JSHandle<JSTaggedValue> 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<JSTaggedValue> self = GetThis(argv);
JSHandle<JSTaggedValue> iter = JSMapIterator::CreateMapIterator(thread, self, IterationKind::VALUE);
return iter.GetTaggedValue();
}
} // namespace panda::ecmascript::builtins

View File

@ -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

View File

@ -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 <cmath>
#include <random>
#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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> msgY = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> 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<double>::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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<float>(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<JSTaggedValue> 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<JSTaggedValue> msg1 = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> 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<int32_t>(static_cast<int64_t>(value1) * static_cast<int64_t>(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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<uint64_t>(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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> msgX = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> 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<double> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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

View File

@ -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

View File

@ -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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSObject> result =
thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle<JSFunction>::Cast(constructor), newTarget);
// 6. ReturnIfAbrupt(O).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 7. Set the value of Os [[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<JSTaggedValue> 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<JSTaggedValue> 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) ≤ 2531, 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<JSTaggedValue> msg = GetCallArg(argv, 0);
if (msg->IsUndefined()) {
return GetTaggedDouble(base::NAN_VALUE);
}
[[maybe_unused]] EcmaHandleScope handleScope(thread);
// 1. Let inputString be ToString(string).
JSHandle<EcmaString> numberString = JSTaggedValue::ToString(thread, msg);
// 2. ReturnIfAbrupt(inputString).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
Span<const uint8_t> str;
if (UNLIKELY(numberString->IsUtf16())) {
size_t len = base::utf_helper::Utf16ToUtf8Size(numberString->GetDataUtf16(), numberString->GetLength()) - 1;
CVector<uint8_t> buf(len);
len = base::utf_helper::ConvertRegionUtf16ToUtf8(numberString->GetDataUtf16(), buf.data(),
numberString->GetLength(), len, 0);
str = Span<const uint8_t>(buf.data(), len);
} else {
str = Span<const uint8_t>(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<JSTaggedValue> msg = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> 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<EcmaString> numberString = JSTaggedValue::ToString(thread, msg);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
Span<const uint8_t> str;
if (UNLIKELY(numberString->IsUtf16())) {
size_t len = base::utf_helper::Utf16ToUtf8Size(numberString->GetDataUtf16(), numberString->GetLength()) - 1;
CVector<uint8_t> buf(len);
len = base::utf_helper::ConvertRegionUtf16ToUtf8(numberString->GetDataUtf16(), buf.data(),
numberString->GetLength(), len, 0);
str = Span<const uint8_t>(buf.data(), len);
} else {
str = Span<const uint8_t>(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<JSTaggedValue> 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<int>(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<JSTaggedValue> 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<int>(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<JSTaggedValue> 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<int>(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<JSTaggedValue> 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<int>(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<JSTaggedValue> 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

View File

@ -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

View File

@ -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<GlobalEnv> env = ecmaVm->GetGlobalEnv();
// 1.If NewTarget is neither undefined nor the active function, then
// a.Return OrdinaryCreateFromConstructor(NewTarget, "%ObjectPrototype%").
JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
if (!newTarget->IsUndefined() && !(newTarget.GetTaggedValue() == constructor.GetTaggedValue())) {
JSHandle<JSObject> obj =
ecmaVm->GetFactory()->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
return obj.GetTaggedValue();
}
// 2.If value is null, undefined or not supplied, return ObjectCreate(%ObjectPrototype%).
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
if (value->IsNull() || value->IsUndefined()) {
JSHandle<JSObject> 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<JSTaggedValue> target = GetCallArg(argv, 0);
JSHandle<JSObject> 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<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
for (array_size_t i = 1; i < numArgs; i++) {
JSHandle<JSTaggedValue> source = GetCallArg(argv, i);
if (!source->IsNull() && !source->IsUndefined()) {
JSHandle<JSObject> from = JSTaggedValue::ToObject(thread, source);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<TaggedArray> keys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle<JSTaggedValue>::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<JSTaggedValue>::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<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &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<JSObject> props = JSTaggedValue::ToObject(thread, prop);
// 3.ReturnIfAbrupt(props).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 4.Let keys be props.[[OwnPropertyKeys]]().
JSHandle<TaggedArray> handleKeys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle<JSTaggedValue>::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<TaggedArray> 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<JSTaggedValue> handleKey(thread, handleKeys->Get(i));
bool success = JSTaggedValue::GetOwnProperty(thread, JSHandle<JSTaggedValue>::Cast(props), handleKey, propDesc);
// ReturnIfAbrupt(propDesc)
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (success && propDesc.IsEnumerable()) {
JSHandle<JSTaggedValue> descObj =
JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::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<JSTaggedValue> 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<JSTaggedValue> properties = GetCallArg(argv, 1);
// 2.Let obj be ObjectCreate(O).
JSHandle<JSObject> 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<JSTaggedValue>::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<JSTaggedValue> 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<JSTaggedValue> prop = GetCallArg(argv, 1);
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSObject>(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<JSTaggedValue> func = GetCallArg(argv, 0);
JSHandle<JSObject> handle = JSTaggedValue::ToObject(thread, func);
// 2.ReturnIfAbrupt(obj).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 3.Let key be ToPropertyKey(P).
JSHandle<JSTaggedValue> prop = GetCallArg(argv, 1);
JSHandle<JSTaggedValue> 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<JSTaggedValue>::Cast(handle), key, desc);
// 6.ReturnIfAbrupt(desc).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 7.Return FromPropertyDescriptor(desc).
JSHandle<JSTaggedValue> res = JSObject::FromPropertyDescriptor(thread, desc);
return res.GetTaggedValue();
}
// Runtime Semantics
JSTaggedValue BuiltinsObject::GetOwnPropertyKeys(JSThread *thread, const JSHandle<JSTaggedValue> &object,
const KeyType &type)
{
BUILTINS_API_TRACE(thread, Object, GetOwnPropertyKeys);
// 1.Let obj be ToObject(O).
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSObject> obj = JSTaggedValue::ToObject(thread, object);
// 2.ReturnIfAbrupt(obj).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 3.Let keys be obj.[[OwnPropertyKeys]]().
JSHandle<TaggedArray> handleKeys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle<JSTaggedValue>::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<TaggedArray> 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<TaggedArray> resultList = factory->CopyArray(nameList, length, copyLength);
JSHandle<JSArray> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> func = GetCallArg(argv, 0);
JSHandle<JSObject> 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<JSTaggedValue> 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<JSObject>(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<JSTaggedValue> 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<JSObject>(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<JSTaggedValue> msg = GetCallArg(argv, 0);
JSHandle<JSObject> obj = JSTaggedValue::ToObject(thread, msg);
// 2. ReturnIfAbrupt(obj).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 3. Let nameList be EnumerableOwnNames(obj).
JSHandle<TaggedArray> nameList = JSObject::EnumerableOwnNames(thread, obj);
// 4. ReturnIfAbrupt(nameList).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 5. Return CreateArrayFromList(nameList).
JSHandle<JSArray> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSObject> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> prop = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> property = JSTaggedValue::ToPropertyKey(thread, prop);
// 2. ReturnIfAbrupt(P).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 3. Let O be ToObject(this value).
JSHandle<JSObject> 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<JSTaggedValue>::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<JSTaggedValue> msg = GetCallArg(argv, 0);
if (!msg->IsECMAObject()) {
return GetTaggedBoolean(false);
}
[[maybe_unused]] EcmaHandleScope handleScope(thread);
// 2. Let O be ToObject(this value).
JSHandle<JSObject> 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<JSTaggedValue> msg = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> property = JSTaggedValue::ToPropertyKey(thread, msg);
// 2. ReturnIfAbrupt(P).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 3. Let O be ToObject(this value).
JSHandle<JSObject> 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<JSTaggedValue>::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<JSTaggedValue> object = GetThis(argv);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread());
// 2. Return Invoke(O, "toString").
JSHandle<JSTaggedValue> calleeKey = thread->GlobalConstants()->GetHandledToStringString();
return JSFunction::Invoke(thread, object, calleeKey, BuiltinsBase::GetArgsArray(argv));
}
JSTaggedValue BuiltinsObject::GetBuiltinTag(JSThread *thread, const JSHandle<JSObject> &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<EcmaString> 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<JSTaggedValue> 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<JSObject> object = JSTaggedValue::ToObject(thread, GetThis(argv));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> builtinTag(thread, GetBuiltinTag(thread, object));
// 16. Let tag be Get (O, @@toStringTag).
auto ecmaVm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
auto factory = ecmaVm->GetFactory();
JSHandle<JSTaggedValue> 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<EcmaString> leftString(factory->NewFromString("[object "));
JSHandle<EcmaString> rightString(factory->NewFromString("]"));
JSHandle<EcmaString> 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<JSObject> 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<JSObject> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSRealm> 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<JSTaggedValue> obj = GetCallArg(argv, 0);
JSHandle<JSObject> object = JSTaggedValue::ToObject(thread, obj);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key+value).
JSHandle<TaggedArray> 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

View File

@ -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<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &prop);
static JSTaggedValue GetOwnPropertyKeys(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const KeyType &type);
static JSTaggedValue GetBuiltinTag(JSThread *thread, const JSHandle<JSObject> &object);
};
} // namespace panda::ecmascript::builtins
#endif // PANDA_RUNTIME_ECMASCRIPT_H

View File

@ -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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSPromise> instancePromise =
JSHandle<JSPromise>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(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<ResolvingFunctionsRecord> resolvingFunction = JSPromise::CreateResolvingFunctions(thread, instancePromise);
// 9. Let completion be Call(executor, undefined, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[reject]])
JSHandle<TaggedArray> 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<JSTaggedValue> thisValue = globalConst->GetHandledUndefined();
JSTaggedValue taggedValue = JSFunction::Call(thread, executor, thisValue, arguments);
JSHandle<JSTaggedValue> 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<TaggedArray> arrayCompletion = factory->NewTaggedArray(length);
arrayCompletion->Set(thread, 0, completionValue);
JSHandle<JSTaggedValue> 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<GlobalEnv> env = ecmaVm->GetGlobalEnv();
ObjectFactory *factory = ecmaVm->GetFactory();
// 1. Let C be the this value.
JSHandle<JSTaggedValue> 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<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
JSHandle<JSTaggedValue> 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<PromiseCapability> capa = JSPromise::NewPromiseCapability(thread, ctor);
// 7. ReturnIfAbrupt(promiseCapability).
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, capa.GetTaggedValue());
// 8. Let iterator be GetIterator(iterable).
JSHandle<JSTaggedValue> 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<JSTaggedValue> done(thread, JSTaggedValue::False());
JSHandle<PromiseIteratorRecord> itRecord = factory->NewPromiseIteratorRecord(itor, done);
// 11. Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability).
JSHandle<CompletionRecord> 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<JSTaggedValue> closeVal =
JSIterator::IteratorClose(thread, itor, JSHandle<JSTaggedValue>::Cast(result));
if (closeVal.GetTaggedValue().IsRecord()) {
result = JSHandle<CompletionRecord>::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<GlobalEnv> env = ecmaVm->GetGlobalEnv();
// 1. Let C be the this value.
// 2. If Type(C) is not Object, throw a TypeError exception.
JSHandle<JSTaggedValue> 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<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
JSHandle<JSTaggedValue> 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> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 8. Let iterator be GetIterator(iterable).
// 9. IfAbruptRejectPromise(iterator, promiseCapability).
JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> 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<JSTaggedValue> done(thread, JSTaggedValue::False());
JSHandle<PromiseIteratorRecord> 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<CompletionRecord> result = PerformPromiseRace(thread, iteratorRecord, promiseCapability, thisValue);
if (result->IsThrow()) {
thread->ClearException();
if (iteratorRecord->GetDone().IsFalse()) {
JSHandle<JSTaggedValue> value =
JSIterator::IteratorClose(thread, iterator, JSHandle<JSTaggedValue>::Cast(result));
if (value.GetTaggedValue().IsCompletionRecord()) {
result = JSHandle<CompletionRecord>(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<JSTaggedValue> 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<JSTaggedValue> xValue = BuiltinsBase::GetCallArg(argv, 0);
if (xValue->IsJSPromise()) {
JSHandle<JSTaggedValue> ctorKey(globalConst->GetHandledConstructorString());
JSHandle<JSTaggedValue> ctorValue = JSObject::GetProperty(thread, xValue, ctorKey).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (JSTaggedValue::SameValue(ctorValue.GetTaggedValue(), thisValue.GetTaggedValue())) {
JSHandle<JSObject> value = JSHandle<JSObject>::Cast(xValue);
return value.GetTaggedValue();
}
}
// 4. Let promiseCapability be NewPromiseCapability(C).
// 5. ReturnIfAbrupt(promiseCapability).
JSHandle<PromiseCapability> 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<TaggedArray> arrayCompletion = factory->NewTaggedArray(length);
arrayCompletion->Set(thread, 0, xValue);
JSHandle<JSTaggedValue> resolve(thread, promiseCapability->GetResolve());
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
JSFunction::Call(thread, resolve, undefined, arrayCompletion);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 8. Return promiseCapability.[[Promise]].
JSHandle<JSObject> 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<JSTaggedValue> 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> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 5. Let rejectResult be Call(promiseCapability.[[Reject]], undefined, «r»).
// 6. ReturnIfAbrupt(rejectResult).
JSHandle<JSTaggedValue> reason = GetCallArg(argv, 0);
array_size_t length = 1;
JSHandle<TaggedArray> arrayCompletion = factory->NewTaggedArray(length);
arrayCompletion->Set(thread, 0, reason);
JSHandle<JSTaggedValue> reject(thread, promiseCapability->GetReject());
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
JSFunction::Call(thread, reject, undefined, arrayCompletion);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 7. Return promiseCapability.[[Promise]].
JSHandle<JSObject> 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<JSTaggedValue> promise = GetThis(argv);
JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
JSHandle<JSTaggedValue> reject = GetCallArg(argv, 0);
array_size_t length = 2;
JSHandle<TaggedArray> 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<GlobalEnv> env = ecmaVm->GetGlobalEnv();
// 1. Let promise be the this value.
JSHandle<JSTaggedValue> 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<JSObject> promise = JSHandle<JSObject>::Cast(thisValue);
JSHandle<JSTaggedValue> defaultFunc = JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction());
JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, promise, defaultFunc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 5. Let resultCapability be NewPromiseCapability(C).
// 6. ReturnIfAbrupt(resultCapability).
JSHandle<PromiseCapability> resultCapability = JSPromise::NewPromiseCapability(thread, constructor);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> onFulfilled = BuiltinsBase::GetCallArg(argv, 0);
JSHandle<JSTaggedValue> onRejected = BuiltinsBase::GetCallArg(argv, 1);
// 7. Return PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability).
return PerformPromiseThen(thread, JSHandle<JSPromise>::Cast(promise), onFulfilled, onRejected, resultCapability);
}
JSTaggedValue BuiltinsPromise::PerformPromiseThen(JSThread *thread, const JSHandle<JSPromise> &promise,
const JSHandle<JSTaggedValue> &onFulfilled,
const JSHandle<JSTaggedValue> &onRejected,
const JSHandle<PromiseCapability> &capability)
{
auto ecmaVm = thread->GetEcmaVM();
JSHandle<job::MicroJobQueue> job = ecmaVm->GetMicroJobQueue();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
ObjectFactory *factory = ecmaVm->GetFactory();
JSMutableHandle<JSTaggedValue> fulfilled(thread, onFulfilled.GetTaggedValue());
auto globalConst = thread->GlobalConstants();
if (!fulfilled->IsCallable()) {
fulfilled.Update(globalConst->GetIdentityString());
}
JSMutableHandle<JSTaggedValue> rejected(thread, onRejected.GetTaggedValue());
if (!rejected->IsCallable()) {
rejected.Update(globalConst->GetThrowerString());
}
JSHandle<PromiseReaction> fulfillReaction = factory->NewPromiseReaction();
fulfillReaction->SetPromiseCapability(thread, capability.GetTaggedValue());
fulfillReaction->SetHandler(thread, fulfilled.GetTaggedValue());
JSHandle<PromiseReaction> rejectReaction = factory->NewPromiseReaction();
rejectReaction->SetPromiseCapability(thread, capability.GetTaggedValue());
rejectReaction->SetHandler(thread, rejected.GetTaggedValue());
if (JSTaggedValue::SameValue(promise->GetPromiseState(),
JSTaggedValue(static_cast<int32_t>(PromiseStatus::PENDING)))) {
JSHandle<TaggedQueue> fulfillReactions(thread, promise->GetPromiseFulfillReactions());
auto result =
JSTaggedValue(TaggedQueue::Push(thread, fulfillReactions, JSHandle<JSTaggedValue>::Cast(fulfillReaction)));
promise->SetPromiseFulfillReactions(thread, result);
JSHandle<TaggedQueue> rejectReactions(thread, promise->GetPromiseRejectReactions());
result =
JSTaggedValue(TaggedQueue::Push(thread, rejectReactions, JSHandle<JSTaggedValue>::Cast(rejectReaction)));
promise->SetPromiseRejectReactions(thread, result);
} else if (JSTaggedValue::SameValue(promise->GetPromiseState(),
JSTaggedValue(static_cast<int32_t>(PromiseStatus::FULFILLED)))) {
JSHandle<TaggedArray> 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<JSFunction> promiseReactionsJob(env->GetPromiseReactionJob());
job->EnqueueJob(thread, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv);
} else if (JSTaggedValue::SameValue(promise->GetPromiseState(),
JSTaggedValue(static_cast<int32_t>(PromiseStatus::REJECTED)))) {
JSHandle<TaggedArray> 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<JSFunction> promiseReactionsJob(env->GetPromiseReactionJob());
job->EnqueueJob(thread, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv);
}
return capability->GetPromise();
}
JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseAll(JSThread *thread,
const JSHandle<PromiseIteratorRecord> &itRecord,
const JSHandle<JSTaggedValue> &ctor,
const JSHandle<PromiseCapability> &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<PromiseRecord> values = factory->NewPromiseRecord();
JSHandle<TaggedArray> emptyArray = factory->EmptyArray();
values->SetValue(thread, emptyArray);
// 4. Let remainingElementsCount be a new Record { [[value]]: 1 }.
JSHandle<PromiseRecord> remainCnt = factory->NewPromiseRecord();
remainCnt->SetValue(thread, JSTaggedNumber(1));
// 5. Let index be 0.
array_size_t index = 0;
// 6. Repeat
JSHandle<JSTaggedValue> itor(thread, itRecord->GetIterator());
JSMutableHandle<JSTaggedValue> 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<JSArray> jsArrayValues =
JSArray::CreateArrayFromList(thread, JSHandle<TaggedArray>(thread, values->GetValue()));
// 2. Let resolveResult be Call(resultCapability.[[Resolve]], undefined, «valuesArray»).
JSHandle<JSTaggedValue> resCapaFunc(thread, capa->GetResolve());
JSHandle<TaggedArray> argv = factory->NewTaggedArray(1);
argv->Set(thread, 0, jsArrayValues.GetTaggedValue());
JSTaggedValue resolveRes =
JSFunction::Call(thread, resCapaFunc, globalConst->GetHandledUndefined(), argv);
// 3. ReturnIfAbrupt(resolveResult)
JSHandle<JSTaggedValue> resolveAbrupt(thread, resolveRes);
RETURN_COMPLETION_IF_ABRUPT(thread, resolveAbrupt);
}
// iv. Return resultCapability.[[Promise]].
JSHandle<CompletionRecord> resRecord = factory->NewCompletionRecord(
CompletionRecord::NORMAL,
JSHandle<JSTaggedValue>(thread, capa->GetPromise()));
return resRecord;
}
// e. Let nextValue be IteratorValue(next).
JSHandle<JSTaggedValue> 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<ObjectWrapper> wrapperVal(thread, thread->GetException());
JSHandle<JSTaggedValue> throwVal(thread, wrapperVal->GetValue());
nextVal = throwVal;
} else {
nextVal = JSHandle<JSTaggedValue>(thread, thread->GetException());
}
}
// g. ReturnIfAbrupt(nextValue).
RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
// h. Append undefined to values.
JSHandle<TaggedArray> valuesArray =
JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(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<JSTaggedValue> resolveKey = globalConst->GetHandledPromiseResolveString();
JSHandle<TaggedArray> nextValueArray = factory->NewTaggedArray(1);
nextValueArray->Set(thread, 0, nextVal);
JSTaggedValue taggedNextPromise = JSFunction::Invoke(thread, ctor, resolveKey, nextValueArray);
// j. ReturnIfAbrupt(nextPromise).
JSHandle<JSTaggedValue> 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<JSPromiseAllResolveElementFunction> resoleveElement = factory->NewJSPromiseAllResolveElementFunction(
reinterpret_cast<void *>(BuiltinsPromiseHandler::ResolveElementFunction));
// l. Set the [[AlreadyCalled]] internal slot of resolveElement to a new Record {[[value]]: false }.
JSHandle<PromiseRecord> 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<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
JSHandle<TaggedArray> 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<JSTaggedValue> result(thread, taggedResult);
// s. ReturnIfAbrupt(result).
RETURN_COMPLETION_IF_ABRUPT(thread, result);
// t. Set index to index + 1.
++index;
}
}
JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseRace(JSThread *thread,
const JSHandle<PromiseIteratorRecord> &iteratorRecord,
const JSHandle<PromiseCapability> &capability,
const JSHandle<JSTaggedValue> &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<JSTaggedValue> iterator(thread, iteratorRecord->GetIterator());
JSMutableHandle<JSTaggedValue> 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<JSTaggedValue> promise(thread, capability->GetPromise());
JSHandle<CompletionRecord> completionRecord =
factory->NewCompletionRecord(CompletionRecord::NORMAL, promise);
return completionRecord;
}
JSHandle<JSTaggedValue> nextValue = JSIterator::IteratorValue(thread, next);
if (thread->HasPendingException()) {
iteratorRecord->SetDone(thread, JSTaggedValue::True());
nextValue = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_COMPLETION_IF_ABRUPT(thread, nextValue);
JSHandle<JSTaggedValue> resolveStr = globalConst->GetHandledPromiseResolveString();
array_size_t length = 1;
JSHandle<TaggedArray> array = factory->NewTaggedArray(length);
array->Set(thread, 0, nextValue);
JSTaggedValue result = JSFunction::Invoke(thread, constructor, resolveStr, array);
JSHandle<JSTaggedValue> nextPromise(thread, result);
if (thread->HasPendingException()) {
nextPromise = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise);
JSHandle<JSTaggedValue> 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<JSTaggedValue> handleResult(thread, result);
if (thread->HasPendingException()) {
handleResult = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_COMPLETION_IF_ABRUPT(thread, handleResult);
}
}
} // namespace panda::ecmascript::builtins

View File

@ -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<JSPromise> &promise,
const JSHandle<JSTaggedValue> &onFulfilled,
const JSHandle<JSTaggedValue> &onRejected,
const JSHandle<PromiseCapability> &capability);
private:
static JSHandle<CompletionRecord> PerformPromiseAll(JSThread *thread,
const JSHandle<PromiseIteratorRecord> &itRecord,
const JSHandle<JSTaggedValue> &ctor,
const JSHandle<PromiseCapability> &capa);
static JSHandle<CompletionRecord> PerformPromiseRace(JSThread *thread,
const JSHandle<PromiseIteratorRecord> &iteratorRecord,
const JSHandle<PromiseCapability> &capability,
const JSHandle<JSTaggedValue> &constructor);
};
} // namespace panda::ecmascript::builtins
#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_PROMISE_H

View File

@ -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<GlobalEnv> env = ecmaVm->GetGlobalEnv();
// 1. Assert: F has a [[Promise]] internal slot whose value is an Object.
JSHandle<JSPromiseReactionsFunction> resolve = JSHandle<JSPromiseReactionsFunction>::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<JSPromise> resolvePromise(thread, resolve->GetPromise());
JSHandle<PromiseRecord> 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<JSTaggedValue> resolution = BuiltinsBase::GetCallArg(argv, 0);
if (JSTaggedValue::SameValue(resolution.GetTaggedValue(), resolvePromise.GetTaggedValue())) {
JSHandle<JSObject> resolutionError =
factory->GetJSError(ErrorType::TYPE_ERROR, "Resolve: The promise and resolution cannot be the same.");
JSPromise::RejectPromise(thread, resolvePromise, JSHandle<JSTaggedValue>::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<JSTaggedValue> thenKey(thread->GlobalConstants()->GetHandledPromiseThenString());
JSHandle<JSTaggedValue> thenValue = JSObject::GetProperty(thread, resolution, thenKey).GetValue();
if (thread->HasPendingException()) {
if (thread->GetException().IsObjectWrapper()) {
JSHandle<ObjectWrapper> wrapperValue(thread, thread->GetException());
JSHandle<JSTaggedValue> 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<TaggedArray> 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<JSFunction> promiseResolveThenableJob(env->GetPromiseResolveThenableJob());
JSHandle<job::MicroJobQueue> 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<JSPromiseReactionsFunction> reject = JSHandle<JSPromiseReactionsFunction>::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<JSPromise> rejectPromise(thread, reject->GetPromise());
JSHandle<PromiseRecord> alreadyResolved(thread, reject->GetAlreadyResolved());
if (alreadyResolved->GetValue().IsTrue()) {
return JSTaggedValue::Undefined();
}
alreadyResolved->SetValue(thread, JSTaggedValue::True());
// 6. Return RejectPromise(promise, reason).
JSHandle<JSTaggedValue> reason = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> 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<JSPromiseExecutorFunction> executor = JSHandle<JSPromiseExecutorFunction>::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> 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<JSTaggedValue> resolve = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> 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<JSPromiseAllResolveElementFunction> func =
JSHandle<JSPromiseAllResolveElementFunction>::Cast(GetConstructor(argv));
// 1. Let alreadyCalled be the value of F's [[AlreadyCalled]] internal slot.
JSHandle<PromiseRecord> alreadyCalled =
JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(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<JSTaggedValue> index(thread, func->GetIndex());
// 5. Let values be the value of F's [[Values]] internal slot.
JSHandle<PromiseRecord> values = JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, func->GetValues()));
// 6. Let promiseCapability be the value of F's [[Capabilities]] internal slot.
JSHandle<PromiseCapability> capa =
JSHandle<PromiseCapability>::Cast(JSHandle<JSTaggedValue>(thread, func->GetCapabilities()));
// 7. Let remainingElementsCount be the value of F's [[RemainingElements]] internal slot.
JSHandle<PromiseRecord> remainCnt =
JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, func->GetRemainingElements()));
// 8. Set values[index] to x.
JSHandle<TaggedArray> arrayValues =
JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(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<JSArray> jsArrayValues = JSArray::CreateArrayFromList(thread, arrayValues);
// b. Return Call(promiseCapability.[[Resolve]], undefined, «valuesArray»).
JSHandle<JSTaggedValue> capaResolve(thread, capa->GetResolve());
JSHandle<JSTaggedValue> undefine = globalConst->GetHandledUndefined();
JSHandle<TaggedArray> 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<JSTaggedValue> value = GetCallArg(argv, 0);
JSHandle<JSAsyncAwaitStatusFunction> 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<JSTaggedValue> reason = GetCallArg(argv, 0);
JSHandle<JSAsyncAwaitStatusFunction> func(GetConstructor(argv));
return JSAsyncAwaitStatusFunction::AsyncFunctionAwaitRejected(argv->GetThread(), func, reason).GetTaggedValue();
}
} // namespace panda::ecmascript::builtins

View File

@ -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

View File

@ -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<JSTaggedValue> value = GetCallArg(argv, 0);
ASSERT(value->IsPromiseReaction());
JSHandle<PromiseReaction> reaction = JSHandle<PromiseReaction>::Cast(value);
JSHandle<JSTaggedValue> argument = GetCallArg(argv, 1);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
// 2. Let promiseCapability be reaction.[[Capabilities]].
JSHandle<PromiseCapability> capability(thread, reaction->GetPromiseCapability());
// 3. Let handler be reaction.[[Handler]].
JSHandle<JSTaggedValue> handler(thread, reaction->GetHandler());
JSHandle<JSTaggedValue> thisValue = globalConst->GetHandledUndefined();
JSMutableHandle<JSTaggedValue> call(thread, capability->GetResolve());
JSHandle<TaggedArray> 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<EcmaString>(),
globalConst->GetHandledThrowerString().GetObject<EcmaString>())) {
call.Update(capability->GetReject());
}
} else {
// 6. Else, let handlerResult be Call(handler, undefined, «argument»).
JSHandle<TaggedArray> 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<JSTaggedValue> 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<JSTaggedValue> promise = GetCallArg(argv, 0);
ASSERT(promise->IsJSPromise());
// 1. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve).
JSHandle<ResolvingFunctionsRecord> resolvingFunctions =
JSPromise::CreateResolvingFunctions(thread, JSHandle<JSPromise>::Cast(promise));
JSHandle<JSTaggedValue> thenable = GetCallArg(argv, 1);
JSHandle<JSTaggedValue> then = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
// 2. Let thenCallResult be Call(then, thenable, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»).
JSHandle<TaggedArray> 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<JSTaggedValue> 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<JSTaggedValue> reject(thread, resolvingFunctions->GetRejectFunction());
JSHandle<TaggedArray> argument = factory->NewTaggedArray(1);
JSHandle<JSTaggedValue> 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

View File

@ -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

View File

@ -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<JSTaggedValue> 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<JSProxy> 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<JSProxy> 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<JSProxyRevocFunction> revoker = thread->GetEcmaVM()->GetFactory()->NewJSProxyRevocFunction(
proxy, reinterpret_cast<void *>(InvalidateProxyFunction));
// 5.Let result be ObjectCreate(%ObjectPrototype%).
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> proto = env->GetObjectFunctionPrototype();
JSHandle<JSObject> result = thread->GetEcmaVM()->GetFactory()->OrdinaryNewJSObjectCreate(proto);
// 6.Perform CreateDataProperty(result, "proxy", p).
auto globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> proxyKey = globalConst->GetHandledProxyString();
JSObject::CreateDataProperty(thread, result, proxyKey, JSHandle<JSTaggedValue>(proxy));
// 7.Perform CreateDataProperty(result, "revoke", revoker).
JSHandle<JSTaggedValue> revokeKey = globalConst->GetHandledRevokeString();
JSObject::CreateDataProperty(thread, result, revokeKey, JSHandle<JSTaggedValue>(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<JSObject> revoke_obj(GetThis(argv));
JSHandle<JSTaggedValue> revokeKey = thread->GlobalConstants()->GetHandledRevokeString();
PropertyDescriptor desc(thread);
JSObject::GetOwnProperty(thread, revoke_obj, revokeKey, desc);
JSProxyRevocFunction::ProxyRevocFunctions(thread, JSHandle<JSProxyRevocFunction>(desc.GetValue()));
return JSTaggedValue::Undefined();
}
} // namespace panda::ecmascript::builtins

View File

@ -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

View File

@ -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<JSTaggedValue> 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<JSTaggedValue> thisArgument = GetCallArg(argv, 1);
JSHandle<JSTaggedValue> argumentsList = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
JSHandle<JSTaggedValue> argOrAbrupt = JSObject::CreateListFromArrayLike(thread, argumentsList);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<TaggedArray> args = JSHandle<TaggedArray>::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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> argumentsList = GetCallArg(argv, 1);
JSHandle<JSTaggedValue> argOrAbrupt = JSObject::CreateListFromArrayLike(thread, argumentsList);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<TaggedArray> args = JSHandle<TaggedArray>::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<JSTaggedValue> 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<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 3. Let desc be ? ToPropertyDescriptor(attributes).
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> val = GetCallArg(argv, 0);
if (!val->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.get target is not object", JSTaggedValue::Exception());
}
JSHandle<JSObject> target = JSHandle<JSObject>::Cast(val);
// 2. Let key be ? ToPropertyKey(propertyKey).
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> val = GetCallArg(argv, 0);
if (!val->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.getPrototypeOf target is not object", JSTaggedValue::Exception());
}
JSHandle<JSObject> target = JSHandle<JSObject>::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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<TaggedArray> keys = JSTaggedValue::GetOwnPropertyKeys(thread, target);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 3. Return CreateArrayFromList(keys).
JSHandle<JSArray> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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<JSTaggedValue> &pattern,
const JSHandle<JSTaggedValue> &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<JSTaggedValue> &regexp,
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<JSTaggedValue> &inputStr, uint32_t index,
bool unicode);
static JSHandle<JSTaggedValue> ConcatFlags(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &string, const char *name);
static bool GetFlagsInternal(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<EcmaString> &flag);
// 21.2.5.2.2 Runtime Semantics: RegExpBuiltinExec ( R, S )
static JSTaggedValue RegExpBuiltinExec(JSThread *thread, const JSHandle<JSTaggedValue> &regexp,
const JSHandle<JSTaggedValue> &inputStr);
// 21.2.5.2.1 Runtime Semantics: RegExpExec ( R, S )
static JSTaggedValue RegExpExec(JSThread *thread, const JSHandle<JSTaggedValue> &regexp,
const JSHandle<JSTaggedValue> &inputString);
// 21.2.3.2.1 Runtime Semantics: RegExpAlloc ( newTarget )
static JSTaggedValue RegExpAlloc(JSThread *thread, const JSHandle<JSTaggedValue> &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<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &pattern, const JSHandle<JSTaggedValue> &flags);
// 21.2.3.2.4 Runtime Semantics: EscapeRegExpPattern ( P, F )
static EcmaString *EscapeRegExpPattern(JSThread *thread, const JSHandle<JSTaggedValue> &src,
const JSHandle<JSTaggedValue> &flag);
static JSTaggedValue RegExpReplaceFast(JSThread *thread, JSHandle<JSTaggedValue> &regexp,
JSHandle<EcmaString> inputString, uint32_t inputLength);
};
class RegExpExecResultCache : public TaggedArray {
public:
enum CacheType {
REPLACE_TYPE,
SPLIT_TYPE,
};
static RegExpExecResultCache *Cast(TaggedObject *object)
{
return reinterpret_cast<RegExpExecResultCache *>(object);
}
static JSTaggedValue CreateCacheTable(JSThread *thread);
JSTaggedValue FindCachedResult(JSThread *thread, const JSHandle<JSTaggedValue> &patten,
const JSHandle<JSTaggedValue> &flag, const JSHandle<JSTaggedValue> &input,
CacheType type);
void AddResultInCache(JSThread *thread, const JSHandle<JSTaggedValue> &patten, const JSHandle<JSTaggedValue> &flag,
const JSHandle<JSTaggedValue> &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

View File

@ -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<JSTaggedValue> 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<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
// 3.returnIfAbrupt()
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSSet> set = JSHandle<JSSet>::Cast(obj);
// 3.ReturnIfAbrupt(set).
// 4.Set sets [[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<JSTaggedValue> 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<JSTaggedValue> adderKey(factory->NewFromString("add"));
JSHandle<JSTaggedValue> setHandle(set);
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> valueIndex(thread, JSTaggedValue(1));
JSHandle<JSTaggedValue> next = JSIterator::IteratorStep(thread, iter);
while (!next->IsFalse()) {
// ReturnIfAbrupt(next).
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue());
// Let nextValue be IteratorValue(next).
JSHandle<JSTaggedValue> nextValue(JSIterator::IteratorValue(thread, next));
// ReturnIfAbrupt(nextValue).
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, nextValue.GetTaggedValue());
JSHandle<TaggedArray> 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<JSTaggedValue>(set), array);
// Let status be Call(adder, set, «nextValue.[[value]]»).
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> value(GetCallArg(argv, 0));
JSHandle<JSSet> 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<JSTaggedValue> 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<JSSet> 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<JSTaggedValue> 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<JSSet> set(thread, JSSet::Cast(*JSTaggedValue::ToObject(thread, self)));
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSSet> set(thread, JSSet::Cast(*JSTaggedValue::ToObject(thread, self)));
// 4.If IsCallable(callbackfn) is false, throw a TypeError exception.
JSHandle<JSTaggedValue> 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<JSTaggedValue> thisArg = GetCallArg(argv, 1);
// composed arguments
int argumentsLength = 3;
JSHandle<TaggedArray> array(factory->NewTaggedArray(argumentsLength));
JSHandle<JSTaggedValue> iter(factory->NewJSSetIterator(set, IterationKind::KEY));
JSHandle<JSTaggedValue> result = JSIterator::IteratorStep(thread, iter);
while (!result->IsFalse()) {
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result.GetTaggedValue());
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> self = GetThis(argv);
JSHandle<JSTaggedValue> 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<JSTaggedValue> self = GetThis(argv);
JSHandle<JSTaggedValue> iter = JSSetIterator::CreateSetIterator(thread, self, IterationKind::VALUE);
return iter.GetTaggedValue();
}
} // namespace panda::ecmascript::builtins

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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<double>(INT_MAX);
constexpr double DOUBLE_INT_MIN = static_cast<double>(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<EcmaString> &matched,
const JSHandle<EcmaString> &srcString, int position,
const JSHandle<TaggedArray> &captureList,
const JSHandle<EcmaString> &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<EcmaString> &str, int32_t q, const JSHandle<EcmaString> &reg);
};
} // namespace panda::ecmascript::builtins
#endif // PANDA_RUNTIME_ECMASCRIPT_BUILTINS_STRING_H

Some files were not shown because too many files have changed in this diff Show More