diff --git a/BUILD.gn b/BUILD.gn
index fee6ad88af7026b60e5ae697a343153985905969..e5a514c74baee27cbfe7c835e2b3660e26920274 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -145,7 +145,10 @@ group("ark_unittest") {
]
if (!run_with_asan) {
if (!(ark_standalone_build && current_os == "ohos")) {
- deps += [ "ecmascript/compiler/tests:host_unittest" ]
+ deps += [
+ "ecmascript/compiler/tests:host_unittest",
+ "ecmascript/ohos/tests:host_unittest",
+ ]
}
}
}
@@ -182,13 +185,22 @@ group("ark_runtime_host_unittest") {
deps = []
if (host_os != "mac") {
# js unittest
- deps += [ ":ark_unittest" ]
+ deps += [
+ ":ark_unittest",
+ "$js_root/test/pgotypeinfer:ark_pgotypeinfer_test",
+ ]
# js bytecode test
deps += [ "$js_root/test/moduletest:ark_js_moduletest" ]
# quickfix test
deps += [ "$js_root/test/quickfix:ark_quickfix_test" ]
+ if (!ark_standalone_build && run_regress_test) {
+ deps += [ "$js_root/test/regresstest:ark_regress_test" ]
+ }
+
+ # execution test
+ deps += [ "$js_root/test/executiontest:ark_execution_test" ]
# ts aot test and asm test
if (!run_with_asan) {
@@ -376,8 +388,11 @@ config("ark_jsruntime_common_config") {
}
}
- if (!is_mac && target_os != "ios" && !use_libfuzzer &&
- !(ark_standalone_build && !enable_lto)) {
+ if (is_ohos) {
+ defines += [ "PANDA_TARGET_OHOS" ]
+ }
+
+ if (!is_mac && target_os != "ios" && !use_libfuzzer && !enable_lto_O0) {
cflags_cc += [ "-flto=thin" ]
ldflags += [ "-flto=thin" ]
}
@@ -454,7 +469,7 @@ config("ark_jsruntime_common_config") {
}
}
- if (enable_leak_check) {
+ if (enable_leak_check || is_asan) {
defines += [ "ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK" ]
}
@@ -466,12 +481,27 @@ config("ark_jsruntime_common_config") {
defines += [ "HOOK_ENABLE" ]
}
}
+
+ # is_asan: skynet config; run_with_asan: est_runtime enable asan config
+ if (is_asan) {
+ defines += [ "ECMASCRIPT_ENABLE_DFX_CONFIG" ]
+ }
+
+ code_encrypto_enable = true
+ if (defined(global_parts_info) &&
+ !defined(global_parts_info.security_code_crypto_metadata_process)) {
+ code_encrypto_enable = false
+ }
+ if (code_encrypto_enable) {
+ defines += [ "CODE_ENCRYPTION_ENABLE" ]
+ }
}
# ecmascript unit testcase config
config("ecma_test_config") {
visibility = [
"./ecmascript/*",
+ "./test/executiontest/*",
"./test/fuzztest/*",
]
@@ -480,7 +510,7 @@ config("ecma_test_config") {
"$js_root:ark_jsruntime_common_config",
]
- if (enable_leak_check) {
+ if (enable_leak_check || is_asan) {
defines = [ "ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK" ]
}
@@ -510,9 +540,12 @@ ecma_source = [
"ecmascript/base/atomic_helper.cpp",
"ecmascript/base/builtins_base.cpp",
"ecmascript/base/error_helper.cpp",
+ "ecmascript/base/fast_json_stringifier.cpp",
+ "ecmascript/base/json_helper.cpp",
"ecmascript/base/json_parser.cpp",
"ecmascript/base/json_stringifier.cpp",
"ecmascript/base/number_helper.cpp",
+ "ecmascript/base/path_helper.cpp",
"ecmascript/base/string_helper.cpp",
"ecmascript/base/typed_array_helper.cpp",
"ecmascript/base/utf_helper.cpp",
@@ -557,6 +590,7 @@ ecma_source = [
"ecmascript/builtins/builtins_weak_ref.cpp",
"ecmascript/builtins/builtins_weak_set.cpp",
"ecmascript/byte_array.cpp",
+ "ecmascript/ohos/code_decrypt.cpp",
"ecmascript/compiler/aot_file/elf_builder.cpp",
"ecmascript/compiler/aot_file/elf_reader.cpp",
"ecmascript/compiler/aot_file/an_file_data_manager.cpp",
@@ -566,7 +600,14 @@ ecma_source = [
"ecmascript/compiler/aot_file/binary_buffer_parser.cpp",
"ecmascript/compiler/aot_file/module_section_des.cpp",
"ecmascript/compiler/aot_file/aot_file_manager.cpp",
+ "ecmascript/compiler/aot_snapshot/aot_snapshot.cpp",
+ "ecmascript/compiler/aot_snapshot/snapshot_constantpool_data.cpp",
+ "ecmascript/compiler/aot_snapshot/snapshot_global_data.cpp",
"ecmascript/compiler/pgo_bc_info.cpp",
+ "ecmascript/compiler/pgo_type/pgo_hclass_generator.cpp",
+ "ecmascript/compiler/pgo_type/pgo_type_manager.cpp",
+ "ecmascript/compiler/pgo_type/pgo_type_parser.cpp",
+ "ecmascript/compiler/pgo_type/pgo_type_recorder.cpp",
"ecmascript/containers/containers_arraylist.cpp",
"ecmascript/containers/containers_deque.cpp",
"ecmascript/containers/containers_errors.cpp",
@@ -598,6 +639,7 @@ ecma_source = [
"ecmascript/ecma_string.cpp",
"ecmascript/ecma_string_table.cpp",
"ecmascript/ecma_vm.cpp",
+ "ecmascript/elements.cpp",
"ecmascript/frames.cpp",
"ecmascript/free_object.cpp",
"ecmascript/generator_helper.cpp",
@@ -615,6 +657,8 @@ ecma_source = [
"ecmascript/interpreter/interpreter_assembly.cpp",
"ecmascript/interpreter/slow_runtime_stub.cpp",
"ecmascript/intl/locale_helper.cpp",
+ "ecmascript/jit/jit.cpp",
+ "ecmascript/jit/jit_task.cpp",
"ecmascript/jobs/micro_job_queue.cpp",
"ecmascript/jspandafile/js_pandafile.cpp",
"ecmascript/jspandafile/js_pandafile_manager.cpp",
@@ -708,12 +752,14 @@ ecma_source = [
"ecmascript/mem/heap_region_allocator.cpp",
"ecmascript/mem/incremental_marker.cpp",
"ecmascript/mem/linear_space.cpp",
+ "ecmascript/mem/machine_code.cpp",
"ecmascript/mem/mem_controller.cpp",
"ecmascript/mem/mem_map_allocator.cpp",
"ecmascript/mem/native_area_allocator.cpp",
"ecmascript/mem/parallel_evacuator.cpp",
"ecmascript/mem/parallel_marker.cpp",
"ecmascript/mem/partial_gc.cpp",
+ "ecmascript/mem/regexp_cached_chunk.cpp",
"ecmascript/mem/stw_young_gc.cpp",
"ecmascript/mem/space.cpp",
"ecmascript/mem/sparse_space.cpp",
@@ -725,21 +771,30 @@ ecma_source = [
"ecmascript/module/js_module_namespace.cpp",
"ecmascript/module/js_module_record.cpp",
"ecmascript/module/js_module_source_text.cpp",
+ "ecmascript/module/js_module_deregister.cpp",
"ecmascript/module/module_data_extractor.cpp",
+ "ecmascript/module/module_path_helper.cpp",
"ecmascript/napi/jsnapi.cpp",
"ecmascript/object_factory.cpp",
"ecmascript/object_operator.cpp",
"ecmascript/patch/patch_loader.cpp",
"ecmascript/patch/quick_fix_manager.cpp",
+ "ecmascript/pgo_profiler/ap_file/pgo_file_info.cpp",
"ecmascript/pgo_profiler/pgo_profiler.cpp",
"ecmascript/pgo_profiler/pgo_profiler_decoder.cpp",
"ecmascript/pgo_profiler/pgo_profiler_encoder.cpp",
"ecmascript/pgo_profiler/pgo_profiler_info.cpp",
"ecmascript/pgo_profiler/pgo_profiler_layout.cpp",
+ "ecmascript/pgo_profiler/pgo_profiler_manager.cpp",
+ "ecmascript/pgo_profiler/pgo_utils.cpp",
+ "ecmascript/pgo_profiler/ap_file/pgo_method_type_set.cpp",
+ "ecmascript/pgo_profiler/types/pgo_profile_type.cpp",
+ "ecmascript/property_accessor.cpp",
"ecmascript/stackmap/ark_stackmap_builder.cpp",
"ecmascript/stackmap/ark_stackmap_parser.cpp",
"ecmascript/stackmap/llvm_stackmap_parser.cpp",
"ecmascript/stackmap/llvm_stackmap_type.cpp",
+ "ecmascript/stackmap/litecg_stackmap_type.cpp",
"ecmascript/subtyping_operator.cpp",
"ecmascript/taskpool/taskpool.cpp",
"ecmascript/taskpool/runner.cpp",
@@ -784,6 +839,7 @@ if (is_ohos && is_standard_system && enable_hitrace) {
ecma_debugger_source = [
"ecmascript/debugger/debugger_api.cpp",
"ecmascript/debugger/js_debugger.cpp",
+ "ecmascript/debugger/dropframe_manager.cpp",
"ecmascript/debugger/hot_reload_manager.cpp",
]
@@ -815,9 +871,24 @@ ecma_profiler_source += [
"ecmascript/snapshot/mem/snapshot_processor.cpp",
]
+ecma_stackinfo_source = []
+
+ecma_stackinfo_source = [
+ "ecmascript/extractortool/src/extractor.cpp",
+ "ecmascript/extractortool/src/file_mapper.cpp",
+ "ecmascript/extractortool/src/file_path_utils.cpp",
+ "ecmascript/extractortool/src/zip_file_reader_io.cpp",
+ "ecmascript/extractortool/src/zip_file_reader_mem.cpp",
+ "ecmascript/extractortool/src/zip_file_reader.cpp",
+ "ecmascript/extractortool/src/zip_file.cpp",
+]
+
ecma_platform_source = []
-ecma_platform_source += [ "ecmascript/platform/common/map.cpp" ]
+ecma_platform_source += [
+ "ecmascript/platform/common/map.cpp",
+ "ecmascript/platform/common/mutex.cpp",
+]
config("include_llvm") {
if (compile_llvm_online) {
@@ -866,6 +937,7 @@ if (is_mingw) {
"ecmascript/platform/windows/file.cpp",
"ecmascript/platform/windows/os.cpp",
"ecmascript/platform/windows/time.cpp",
+ "ecmascript/platform/windows/log.cpp",
]
} else {
ecma_platform_source += [
@@ -873,20 +945,29 @@ if (is_mingw) {
"ecmascript/platform/unix/map.cpp",
"ecmascript/platform/unix/time.cpp",
]
- if (is_mac || target_os == "ios") {
+ if (is_mac) {
ecma_platform_source += [
"ecmascript/platform/unix/mac/os.cpp",
"ecmascript/platform/unix/mac/backtrace.cpp",
+ "ecmascript/platform/unix/mac/log.cpp",
+ ]
+ } else if (target_os == "ios") {
+ ecma_platform_source += [
+ "ecmascript/platform/unix/mac/os.cpp",
+ "ecmascript/platform/unix/mac/backtrace.cpp",
+ "ecmascript/platform/common/log.cpp",
]
} else if (is_ohos || target_os == "android") {
ecma_platform_source += [
"ecmascript/platform/unix/linux/os.cpp",
"ecmascript/platform/unix/ohos/backtrace.cpp",
+ "ecmascript/platform/common/log.cpp",
]
} else if (is_linux) {
ecma_platform_source += [
"ecmascript/platform/unix/linux/os.cpp",
"ecmascript/platform/unix/linux/backtrace.cpp",
+ "ecmascript/platform/common/log.cpp",
]
} else {
print("Invalid os!")
@@ -915,12 +996,20 @@ ohos_source_set("libark_jsruntime_set") {
"/system/lib64/${arkcompiler_relative_lib_path}/lib_ark_builtins.d.abc"
defines += [ "TARGET_BUILTINS_DTS_PATH=\"${target_builtins_dts_path}\"" ]
+ if (current_cpu == "arm64") {
+ defines += [ "ENABLE_POSTFORK_FORCEEXPAND" ]
+ }
+
sources = ecma_source
sources += ecma_profiler_source
sources += ecma_debugger_source
sources += hitrace_scope_source
sources += ecma_platform_source
+ if (is_ohos) {
+ sources += ecma_stackinfo_source
+ }
+
public_configs = [ "$js_root:ark_jsruntime_public_config" ]
public_configs += [ ":include_llvm" ]
@@ -936,12 +1025,18 @@ ohos_source_set("libark_jsruntime_set") {
external_deps = []
deps = []
- if (!is_cross_platform_build) {
+ if (!is_arkui_x) {
external_deps += [ "runtime_core:arkfile_header_deps" ]
} else {
deps += [ "$ark_root/libpandafile:arkfile_header_deps" ]
}
+ if (is_ohos && is_standard_system && !is_arkui_x &&
+ defined(global_parts_info) && defined(global_parts_info.qos_manager)) {
+ defines += [ "ENABLE_QOS" ]
+ external_deps += [ "qos_manager:qos" ]
+ }
+
if (enable_target_compilation) {
external_deps += [ "c_utils:utils" ]
}
@@ -975,7 +1070,7 @@ ohos_source_set("libark_js_intl_set") {
external_deps = []
deps = []
- if (!is_cross_platform_build) {
+ if (!is_arkui_x) {
external_deps += [ "runtime_core:arkfile_header_deps" ]
} else {
deps += [ "$ark_root/libpandafile:arkfile_header_deps" ]
@@ -1003,6 +1098,11 @@ ohos_source_set("libark_jsruntime_test_set") {
sources += ecma_platform_source
defines = [ "OHOS_UNIT_TEST" ]
+
+ if (is_ohos) {
+ sources += ecma_stackinfo_source
+ }
+
if (is_ohos && is_standard_system) {
stub_an_file_path = "/system/lib64/${arkcompiler_relative_lib_path}/stub.an"
} else {
@@ -1019,7 +1119,7 @@ ohos_source_set("libark_jsruntime_test_set") {
"$ark_third_party_root/icu/icu4c:shared_icuuc",
]
external_deps = []
- if (!is_cross_platform_build) {
+ if (!is_arkui_x) {
external_deps += [ "runtime_core:arkfile_header_deps" ]
} else {
deps += [ "$ark_root/libpandafile:arkfile_header_deps" ]
@@ -1038,7 +1138,7 @@ ohos_source_set("libark_jsruntime_test_set") {
deps += [ "$js_root/ecmascript/compiler:libark_mock_stub_set" ]
}
- if (enable_leak_check) {
+ if (enable_leak_check || is_asan) {
defines += [ "ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK" ]
}
@@ -1070,7 +1170,7 @@ ohos_source_set("libark_jsruntime_static") {
":libark_jsruntime_set",
]
external_deps = []
- if (!is_cross_platform_build) {
+ if (!is_arkui_x) {
external_deps += [ "runtime_core:arkfile_header_deps" ]
} else {
deps += [ "$ark_root/libpandafile:arkfile_header_deps" ]
@@ -1097,7 +1197,7 @@ ohos_shared_library("libark_jsruntime") {
sdk_libc_secshared_dep,
]
external_deps = []
- if (!is_cross_platform_build) {
+ if (!is_arkui_x) {
external_deps += [ "runtime_core:libarkfile_static" ]
} else {
deps += [ "$ark_root/libpandafile:libarkfile_static" ]
@@ -1171,3 +1271,12 @@ ohos_shared_library("libark_jsruntime_test") {
}
subsystem_name = "test"
}
+
+ohos_prebuilt_etc("app_aot_white_list") {
+ relative_install_dir = "ark"
+ source = "$js_root/ecmascript/ohos/app_aot_white_list.conf"
+
+ # Set the subsystem name
+ part_name = "ets_runtime"
+ subsystem_name = "arkcompiler"
+}
diff --git a/README_zh.md b/README_zh.md
index 5cf324c0323ca4672643a49786f39c8527aa811d..2f3e5112f0bdce3ec3017f4d2b10fe102687ab04 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -69,7 +69,7 @@ NAPI接口说明参考[NAPI部件](https://gitee.com/openharmony/arkui_napi/blob
### 使用说明
-ArkTS生成字节码参考[方舟eTS编译器](docs/using-the-toolchain-zh.md)
+ArkTS生成字节码参考[方舟eTS编译器]( https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/README_zh.md#%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E)
字节码执行:
```
diff --git a/bundle.json b/bundle.json
index c7af643cffcee1348797ce3be7d19a7394264455..b98bd71a4fdd2e62a4e30beebb649f0e5c0ae751 100644
--- a/bundle.json
+++ b/bundle.json
@@ -25,6 +25,7 @@
"faultloggerd",
"hitrace",
"hilog",
+ "qos_manager",
"runtime_core",
"c_utils",
"code_signature"
@@ -37,6 +38,7 @@
},
"build": {
"sub_component": [
+ "//arkcompiler/ets_runtime:app_aot_white_list",
"//arkcompiler/ets_runtime:ark_js_packages"
],
"inner_kits": [
diff --git a/docs/README_zh.md b/docs/README_zh.md
index 037e4173ea5c8db25e61fbfcb653668e65dd96ed..a7bf762b8c4d55f22ac459954167ac5bb74339d6 100644
--- a/docs/README_zh.md
+++ b/docs/README_zh.md
@@ -106,17 +106,9 @@ print("Hello World!!!");
1. 通过方舟前端生成hello-world.abc文件,编译命令:
```
- node --expose-gc /your_code_path/out/rk3568/clang_x64/arkcompiler/ets_frontend/build/src/index.js hello-world.js
+ /your_code_path/out/rk3568/clang_x64/arkcompiler/ets_frontend/es2abc hello-world.js
```
- **注意**:使用node编译abc过程遇到ENOENT错误,运行如下命令进行修复
-
- ```
- npm cache clean --force
- cd /your_code_path/arkcompiler/ets_frontend/ts2panda
- npm install
- cd /your_code_path/out/rk3568/clang_x64/arkcompiler/ets_frontend/build
- npm install
- ```
+
2. 执行hello-world.abc文件:
1. 设置搜索路径:
@@ -400,7 +392,7 @@ print('Hello World!!!')
1. 通过方舟前端生成hello-world.abc文件,编译命令:
```
- node --expose-gc /your_code_path/out/rk3568/clang_x64/arkcompiler/ets_frontend/build/src/index.js -m --merge-abc test1/test.ts
+ /your_code_path/out/rk3568/clang_x64/arkcompiler/ets_frontend/es2abc --module --merge-abc test1/test.ts
```
2. 执行hello-world.abc文件:
@@ -439,136 +431,151 @@ print('Hello World!!!')
构建编译:
```
-$ ./build.sh --product-name rk3568 --build-target ark_ts2abc_build
+$ ./build.sh --product-name rk3568 --build-target ets_frontend_build
```
-安装 `node`和 `npm`后, 使用前端工具:
-
```
-$ cd out/rk3568/clang_x64/arkcompiler/ets_frontend/build
-$ npm install
-$ node --expose-gc src/index.js [选项] file.js
+$ cd out/rk3568/clang_x64/arkcompiler/ets_frontend/
+$ ./es2abc [options] file.js
```
-
选项
- |
-缩写
+
+
+选项
|
-描述
+ | 描述
|
-取值范围
+ | 取值范围
|
-默认值
+ | 默认值
|
---modules
+ | --debug-info
|
--m
+ | 携带debug信息
|
-按照Module方式编译
+ | -
|
--
- |
--
+ | -
|
---debug-log
- |
--l
+ | --debugger-evaluate-expression
|
-使能log信息
+ | debugger下对输入的base64形式的表达式求值
|
--
+ | -
|
--
+ | -
|
---dump-assembly
- |
--a
+ | --dump-assembly
|
-输出为可读文本格式的字节码文件
+ | 输出为汇编文件
|
--
+ | -
|
--
+ | -
|
---debug
+ | --dump-ast
|
--d
+ | 打印解析得到的ast(抽象语法树)
|
-携带debug信息
+ | -
|
--
- |
--
+ | -
|
---show-statistics
- |
--s
+ | --dump-debug-info
|
-显示字节码相关的统计信息
+ | 打印debug信息
|
--
+ | -
|
--
+ | -
|
---output
+ | --dump-literal-buffer
+ |
+打印literal buffer内容
|
--o
+ | -
+ |
+-
+ |
+
+--dump-size-stat
|
-输出文件路径
+ | 显示字节码相关的统计信息
|
--
+ | -
|
--
+ | -
|
---timeout
+ | --extension
|
--t
+ | 指定输入类型
|
-超时门限
+ | ['js', 'ts', 'as']
|
--
+ | -
|
--
+ |
+--help
+ |
+帮助提示
+ |
+-
+ |
+-
|
---help
+ | --module
+ |
+按照ESM模式编译
+ |
+-
|
--h
+ | -
|
-帮助提示
+ |
+--opt-level
+ |
+指定编译优化等级
|
--
+ | ['0', '1', '2']
|
--
+ | 0
|
---bc-version
+ | --output
+ |
+
+输出文件路径
|
--v
+ | -
+ |
+-
+ |
+
+--parse-only
|
-输出当前字节码版本
+ | 只对输入文件做解析动作
|
--
+ | -
|
--
+ | -
|
---bc-min-version
+ | --thread
|
- |
-输出支持的最低字节码版本
+ | 指定生成字节码时所用的线程数目
|
--
+ | 0-机器支持的线程数目
|
--
+ | 0
|
diff --git a/docs/development-example.md b/docs/development-example.md
index 4fc6e27681fcaf0f7d541c05706645bcde2ff848..c8ee078d03e02cc03cb720233b94e7ae1a988f6c 100644
--- a/docs/development-example.md
+++ b/docs/development-example.md
@@ -48,7 +48,7 @@ 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/rk3568/clang_x64/arkcompiler/ets_frontend/build/src/index.js hello-world.js
+ /your_code_path/out/rk3568/clang_x64/arkcompiler/ets_frontend/es2abc hello-world.js
```
2. Run the **hello-world.abc** file.
diff --git a/docs/using-the-toolchain.md b/docs/using-the-toolchain.md
index d487322f813bd9890ac8a9b75f25af36e467eef1..2b0f65c3dfe5d80e4eeb96bc105aef73c121747b 100644
--- a/docs/using-the-toolchain.md
+++ b/docs/using-the-toolchain.md
@@ -9,137 +9,153 @@ Front-end tools, converting JS source code into ARK bytecode, can be built by sp
Build tools:
```
-$ $ ./build.sh --product-name rk3568 --build-target ark_ts2abc_build
+$ $ ./build.sh --product-name rk3568 --build-target ets_frontend_build
```
-Install `node` and `npm`, then use tools:
-
```
-$ cd out/rk3568/clang_x64/arkcompiler/ets_frontend/build
-$ npm install
-$ node --expose-gc src/index.js [option] file.js
+$ cd out/rk3568/clang_x64/arkcompiler/ets_frontend/
+$ ./es2abc [options] file.js
```
-
-Option
- |
-Abbreviation
+If no parameter is specified for **\[options\]**, an ARK binary file is generated by default.
+
+
+
+Option
|
-Description
+ | Description
|
-Value Range
+ | Value Range
|
-Default Value
+ | Default Value
|
---modules
+ | --debug-info
|
--m
+ | Provides debug information.
|
-Compiles JS files based on the module.
+ | -
|
--
- |
--
+ | -
|
---debug-log
- |
--l
+ | --debugger-evaluate-expression
|
-Enables the log function.
+ | Evaluates base64 style expression in debugger
|
--
+ | -
|
--
+ | -
|
---dump-assembly
- |
--a
+ | --dump-assembly
|
-Outputs a text ARK bytecode file.
+ | Outputs an assembly file.
|
--
+ | -
|
--
+ | -
|
---debug
+ | --dump-ast
|
--d
+ | Prints the parsed AST(Abstract Syntax Tree)
|
-Provides debug information.
+ | -
|
--
- |
--
+ | -
|
---show-statistics
- |
--s
+ | --dump-debug-info
|
-Displays statistics about bytecodes.
+ | Prints debug Info
|
--
+ | -
|
--
+ | -
|
---output
+ | --dump-literal-buffer
+ |
+Prints the content of literal buffer
|
--o
+ | -
+ |
+-
+ |
+
+--dump-size-stat
|
-Specifies the path of the output file.
+ | Displays statistics about bytecodes.
|
--
+ | -
|
--
+ | -
|
---timeout
+ | --extension
|
--t
+ | Specifies input file type
|
-Specifies the timeout threshold.
+ | ['js', 'ts', 'as']
|
--
+ | -
|
--
+ |
+--help
+ |
+Displays help information.
+ |
+-
+ |
+-
|
---help
+ | --module
+ |
+Compiles the code based on the ecmascript standard module.
+ |
+-
|
--h
+ | -
|
-Displays help information.
+ |
+--opt-level
+ |
+Specifies the level for compilation optimization.
|
--
+ | ['0', '1', '2']
|
--
+ | 0
|
---bc-version
+ | --output
+ |
+
+Specifies the path of the output file.
|
--v
+ | -
+ |
+-
+ |
+
+--parse-only
|
-Outputs the current bytecode version.
+ | Parse the input file only
|
--
+ | -
|
--
+ | -
|
---bc-min-version
+ | --thread
|
- |
-Outputs the lowest bytecode version supported.
+ | Specifies the number of threads used to generate bytecode
|
--
+ | 0-Number of threads supported by your machine
|
--
+ | 0
|
diff --git a/ecmascript/base/array_helper.cpp b/ecmascript/base/array_helper.cpp
index 68fe58c692b56d0886fa3f07851d500696037065..f9ec72a50b3f332d417bac52ca28376889158a55 100644
--- a/ecmascript/base/array_helper.cpp
+++ b/ecmascript/base/array_helper.cpp
@@ -24,8 +24,104 @@
#include "ecmascript/js_hclass.h"
#include "ecmascript/js_tagged_number.h"
#include "ecmascript/js_tagged_value-inl.h"
+#include "ecmascript/object_fast_operator-inl.h"
namespace panda::ecmascript::base {
+int64_t ArrayHelper::GetStartIndex(JSThread *thread, const JSHandle &startIndexHandle,
+ int64_t length)
+{
+ // Common procedure to clamp fromIndexValue to the range [0, length].
+ // For integer case, conditional selection instructions (csel in ARM, cmov in x86, etc.)
+ // may be utilized by the compiler to minimize branching.
+ auto doClamp = [length](auto fromIndexValue) -> int64_t {
+ if (LIKELY(fromIndexValue >= 0)) {
+ // Including the case where fromIndexValue == Infinity
+ return (fromIndexValue >= length) ? length : static_cast(fromIndexValue);
+ }
+ auto plusLength = fromIndexValue + length;
+ if (plusLength >= 0) {
+ return static_cast(plusLength);
+ }
+ return 0; // Including the case where fromIndexValue == -Infinity
+ };
+ if (LIKELY(startIndexHandle->IsInt())) {
+ // Fast path: startIndexHandle is tagged int32.
+ return doClamp(startIndexHandle->GetInt());
+ }
+ // Slow path: startIndexHandle is targged double, or type conversion is involved.
+ JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, startIndexHandle);
+ if (UNLIKELY(thread->HasPendingException())) {
+ return length;
+ }
+ double fromIndexValue = base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber()); // NaN -> 0
+ return doClamp(fromIndexValue);
+}
+
+int64_t ArrayHelper::GetStartIndexFromArgs(JSThread *thread, EcmaRuntimeCallInfo *argv,
+ uint32_t argIndex, int64_t length)
+{
+ uint32_t argc = argv->GetArgsNumber();
+ if (argc <= argIndex) {
+ return 0;
+ }
+ JSHandle arg = base::BuiltinsBase::GetCallArg(argv, argIndex);
+ return GetStartIndex(thread, arg, length);
+}
+
+int64_t ArrayHelper::GetLastStartIndex(JSThread *thread, const JSHandle &startIndexHandle,
+ int64_t length)
+{
+ // Common procedure to clamp fromIndexValue to the range [-1, length-1].
+ auto doClamp = [length](auto fromIndexValue) -> int64_t {
+ if (LIKELY(fromIndexValue >= 0)) {
+ // Including the case where fromIndexValue == Infinity
+ return (length - 1 < fromIndexValue) ? (length - 1) : static_cast(fromIndexValue);
+ }
+ auto plusLength = fromIndexValue + length;
+ if (plusLength >= 0) {
+ return static_cast(plusLength);
+ }
+ return -1; // Including the case where fromIndexValue == -Infinity
+ };
+ if (LIKELY(startIndexHandle->IsInt())) {
+ // Fast path: startIndexHandle is tagged int32.
+ return doClamp(startIndexHandle->GetInt());
+ }
+ // Slow path: startIndexHandle is targged double, or type conversion is involved.
+ JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, startIndexHandle);
+ if (UNLIKELY(thread->HasPendingException())) {
+ return -1;
+ }
+ double fromIndexValue = base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber()); // NaN -> 0
+ return doClamp(fromIndexValue);
+}
+
+int64_t ArrayHelper::GetLastStartIndexFromArgs(JSThread *thread, EcmaRuntimeCallInfo *argv,
+ uint32_t argIndex, int64_t length)
+{
+ uint32_t argc = argv->GetArgsNumber();
+ if (argc <= argIndex) {
+ return length - 1;
+ }
+ JSHandle arg = base::BuiltinsBase::GetCallArg(argv, argIndex);
+ return GetLastStartIndex(thread, arg, length);
+}
+
+bool ArrayHelper::ElementIsStrictEqualTo(JSThread *thread, const JSHandle &thisObjVal,
+ const JSHandle &keyHandle,
+ const JSHandle &target)
+{
+ bool exists = thisObjVal->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, keyHandle);
+ if (thread->HasPendingException() || !exists) {
+ return false;
+ }
+ JSHandle valueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, keyHandle);
+ if (thread->HasPendingException()) {
+ return false;
+ }
+ return JSTaggedValue::StrictEqual(thread, target, valueHandle);
+}
+
bool ArrayHelper::IsConcatSpreadable(JSThread *thread, const JSHandle &obj)
{
// 1. If Type(O) is not Object, return false.
@@ -37,19 +133,22 @@ bool ArrayHelper::IsConcatSpreadable(JSThread *thread, const JSHandleGetEcmaVM();
JSHandle env = ecmaVm->GetGlobalEnv();
JSHandle isConcatsprKey = env->GetIsConcatSpreadableSymbol();
- JSHandle spreadable = JSTaggedValue::GetProperty(thread, obj, isConcatsprKey).GetValue();
+ JSTaggedValue spreadable = ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(),
+ isConcatsprKey.GetTaggedValue());
// 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();
+ if (!spreadable.IsUndefined()) {
+ return spreadable.ToBoolean();
}
// 5. Return IsArray(O).
return obj->IsArray(thread);
}
+// must use 'double' as return type, for sort result may double.
+// let arr = [1,2,3,4,5,6]; arr.sort(() => Math.random() - 0.5);
double ArrayHelper::SortCompare(JSThread *thread, const JSHandle &callbackfnHandle,
const JSHandle &valueX, const JSHandle &valueY)
{
@@ -86,10 +185,10 @@ double ArrayHelper::SortCompare(JSThread *thread, const JSHandle
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
info->SetCallArg(valueX.GetTaggedValue(), valueY.GetTaggedValue());
JSTaggedValue callResult = JSFunction::Call(info);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
if (callResult.IsInt()) {
return callResult.GetInt();
}
- RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
JSHandle testResult(thread, callResult);
JSTaggedNumber v = JSTaggedValue::ToNumber(thread, testResult);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
@@ -106,12 +205,45 @@ double ArrayHelper::SortCompare(JSThread *thread, const JSHandle
// 9. If xString < yString, return -1.
// 10. If xString > yString, return 1.
// 11. Return +0.
+ if (valueX->IsInt() && valueY->IsInt()) {
+ return JSTaggedValue::IntLexicographicCompare(valueX.GetTaggedValue(), valueY.GetTaggedValue());
+ }
+ if (valueX->IsString() && valueY->IsString()) {
+ return EcmaStringAccessor::Compare(thread->GetEcmaVM(),
+ JSHandle(valueX), JSHandle(valueY));
+ }
JSHandle xValueHandle(JSTaggedValue::ToString(thread, valueX));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
JSHandle yValueHandle(JSTaggedValue::ToString(thread, valueY));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
ComparisonResult compareResult = JSTaggedValue::Compare(thread, xValueHandle, yValueHandle);
- return compareResult == ComparisonResult::GREAT ? 1 : 0;
+ if (compareResult == ComparisonResult::GREAT) {
+ return 1;
+ }
+ if (compareResult == ComparisonResult::LESS) {
+ return -1;
+ }
+ return 0;
+}
+
+double ArrayHelper::StringSortCompare(JSThread *thread, const JSHandle &valueX,
+ const JSHandle &valueY)
+{
+ ASSERT(valueX->IsString());
+ ASSERT(valueY->IsString());
+ // 9. If xString < yString, return -1.
+ // 10. If xString > yString, return 1.
+ // 11. Return +0.
+ auto xHandle = JSHandle(valueX);
+ auto yHandle = JSHandle(valueY);
+ int result = EcmaStringAccessor::Compare(thread->GetEcmaVM(), xHandle, yHandle);
+ if (result < 0) {
+ return -1;
+ }
+ if (result > 0) {
+ return 1;
+ }
+ return 0;
}
int64_t ArrayHelper::GetLength(JSThread *thread, const JSHandle &thisHandle)
@@ -187,6 +319,7 @@ JSTaggedValue ArrayHelper::FlattenIntoArray(JSThread *thread, const JSHandle ArrayHelper::SortIndexedProperties(JSThread *thread, const JSHandle &thisObj,
+ int64_t len, const JSHandle &callbackFnHandle,
+ HolesType holes)
+{
+ // 1. Let items be a new empty List.
+ JSHandle items(thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len));
+ // 2. Let k be 0.
+ int64_t k = 0;
+ // 3. Repeat, while k < len,
+ // a. Let Pk be ! ToString(𝔽(k)).
+ // b. If holes is skip-holes, then
+ // i. Let kRead be ? HasProperty(obj, Pk).
+ // c. Else,
+ // i. Assert: holes is read-through-holes.
+ // ii. Let kRead be true.
+ // d. If kRead is true, then
+ // i. Let kValue be ? Get(obj, Pk).
+ // ii. Append kValue to items.
+ // e. Set k to k + 1.
+ bool kRead = false;
+ JSMutableHandle pk(thread, JSTaggedValue::Undefined());
+
+ int64_t index = 0;
+ while (k < len) {
+ if (holes == HolesType::SKIP_HOLES) {
+ pk.Update(JSTaggedValue(k));
+ kRead = JSTaggedValue::HasProperty(thread, thisObj, pk);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, items);
+ } else {
+ ASSERT(holes == HolesType::READ_THROUGH_HOLES);
+ kRead = true;
+ }
+ if (kRead) {
+ JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObj, k);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, items);
+ items->Set(thread, index++, kValue.GetTaggedValue());
+ }
+ ++k;
+ }
+ if (index < k) {
+ items->Trim(thread, index);
+ }
+ // 4. Sort items using an implementation-defined sequence of calls to SortCompare.
+ // If any such call returns an abrupt completion,
+ // stop before performing any further calls to SortCompare and return that Completion Record.
+ JSArray::SortElements(thread, items, callbackFnHandle);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, items);
+ // 5. Return items.
+ return items;
+}
} // namespace panda::ecmascript::base
diff --git a/ecmascript/base/array_helper.h b/ecmascript/base/array_helper.h
index 1461c246cd9d96ff6829d1cdd799e26fe4402377..7417bbd0cfbb76e00a26b8511a4e4af225957c50 100644
--- a/ecmascript/base/array_helper.h
+++ b/ecmascript/base/array_helper.h
@@ -19,6 +19,8 @@
#include
#include "ecmascript/base/builtins_base.h"
+#include "ecmascript/ecma_runtime_call_info.h"
+#include "ecmascript/js_tagged_value.h"
namespace panda::ecmascript::base {
struct FlattenArgs {
@@ -26,17 +28,51 @@ struct FlattenArgs {
int64_t start = 0;
double depth = 0;
};
+
+enum class HolesType {
+ SKIP_HOLES,
+ READ_THROUGH_HOLES,
+};
class ArrayHelper {
public:
+ // Common subprocedure for Array.prototype.at, Array.prototype.indexOf, Array.prototype.slice, etc.
+ // Gets start index that falls in range [0, length].
+ // length is returned on pending exception.
+ static int64_t GetStartIndex(JSThread *thread, const JSHandle &startIndexHandle,
+ int64_t length);
+ // If argIndex is out of range [0, argc), then start index = 0 by default.
+ // Otherwise, let startIndexHandle = GetCallArg(argv, argIndex) and call GetStartIndex.
+ static int64_t GetStartIndexFromArgs(JSThread *thread, EcmaRuntimeCallInfo *argv,
+ uint32_t argIndex, int64_t length);
+ // Common subprocedure for Array.prototype.lastIndexOf, etc.
+ // Gets last start index that falls in range [-1, length - 1].
+ // -1 is returned on pending exception.
+ static int64_t GetLastStartIndex(JSThread *thread, const JSHandle &startIndexHandle,
+ int64_t length);
+ // If argIndex is out of range [0, argc), then start index = length - 1 by default.
+ // Otherwise, let startIndexHandle = GetCallArg(argv, argIndex) and call GetLastStartIndex.
+ static int64_t GetLastStartIndexFromArgs(JSThread *thread, EcmaRuntimeCallInfo *argv,
+ uint32_t argIndex, int64_t length);
+ // Let thisHandle be the array object. Checks whether array[key] (if exists) is strictly equal to target.
+ // Returns false on pending exception.
+ static bool ElementIsStrictEqualTo(JSThread *thread, const JSHandle &thisHandle,
+ const JSHandle &keyHandle,
+ const JSHandle &target);
+
static bool IsConcatSpreadable(JSThread *thread, const JSHandle &obj);
static double SortCompare(JSThread *thread, const JSHandle &callbackfnHandle,
const JSHandle &valueX, const JSHandle &valueY);
+ static double StringSortCompare(JSThread *thread, const JSHandle &valueX,
+ const JSHandle &valueY);
static int64_t GetLength(JSThread *thread, const JSHandle &thisHandle);
static int64_t GetArrayLength(JSThread *thread, const JSHandle &thisHandle);
static JSTaggedValue FlattenIntoArray(JSThread *thread, const JSHandle &newArrayHandle,
const JSHandle &thisObjVal, const FlattenArgs &args,
const JSHandle &mapperFunctionHandle,
const JSHandle &thisArg);
+ static JSHandle SortIndexedProperties(JSThread *thread, const JSHandle &thisObj,
+ int64_t len, const JSHandle &callbackFnHandle,
+ HolesType holes);
};
} // namespace panda::ecmascript::base
diff --git a/ecmascript/base/atomic_helper.h b/ecmascript/base/atomic_helper.h
index bc9ef981f7f3f9877ae7539033370eb24266fe6a..c180bc7e3bdfeb98b94e01cd9c6064e725578d3c 100644
--- a/ecmascript/base/atomic_helper.h
+++ b/ecmascript/base/atomic_helper.h
@@ -19,7 +19,7 @@
#include "ecmascript/js_dataview.h"
namespace panda::ecmascript::base {
-enum class BytesSize : int32_t {ONEBYTES = 1, TWOBYTES = 2, FOURBYTES = 4, EIGHTBYTES = 8};
+enum class BytesSize : uint32_t {ONEBYTES = 1, TWOBYTES = 2, FOURBYTES = 4, EIGHTBYTES = 8};
class AtomicHelper final {
public:
@@ -101,4 +101,4 @@ public:
};
} // namespace panda::ecmascript::base
-#endif // ECMASCRIPT_BASE_ATOMIC_HELPER_H
\ No newline at end of file
+#endif // ECMASCRIPT_BASE_ATOMIC_HELPER_H
diff --git a/ecmascript/base/bit_helper.h b/ecmascript/base/bit_helper.h
index 1bc058b0981b8e4297937afcd481feb810033884..fe9ffbeb10865a14984f7ec88ad96fe478c0e33e 100644
--- a/ecmascript/base/bit_helper.h
+++ b/ecmascript/base/bit_helper.h
@@ -22,6 +22,7 @@
#include
namespace panda::ecmascript::base {
+constexpr uint64_t pureNaN = 0x7FF8ULL << 48U; // Be sure return the NaN that is safe.
template
union Data {
S src;
diff --git a/ecmascript/base/builtins_base.h b/ecmascript/base/builtins_base.h
index be50bb83cc00234aa705531d52f3a8d29a487dd1..baf84b1858ea4f49d48274e3d575a73587eb921e 100644
--- a/ecmascript/base/builtins_base.h
+++ b/ecmascript/base/builtins_base.h
@@ -30,6 +30,96 @@
namespace panda::ecmascript {
class JSArray;
namespace base {
+class BuiltinConstantEntry {
+public:
+ constexpr BuiltinConstantEntry(std::string_view name, JSTaggedValue value)
+ : name_(name), rawTaggedValue_(value.GetRawData()) {}
+
+ static constexpr BuiltinConstantEntry Create(std::string_view name, JSTaggedValue value)
+ {
+ return BuiltinConstantEntry(name, value);
+ }
+
+ constexpr std::string_view GetName() const
+ {
+ return name_;
+ }
+
+ constexpr JSTaggedValue GetTaggedValue() const
+ {
+ return JSTaggedValue(rawTaggedValue_);
+ }
+
+private:
+ std::string_view name_;
+ JSTaggedType rawTaggedValue_;
+};
+
+class BuiltinFunctionEntry {
+public:
+ static constexpr int LENGTH_BITS_SIZE = 8;
+ static constexpr int BUILTIN_ID_BITS_SIZE = 8;
+ // Assures the bits are enough to represent all builtin stubs.
+ static_assert(kungfu::BuiltinsStubCSigns::NUM_OF_BUILTINS_STUBS <= (1u << BUILTIN_ID_BITS_SIZE));
+
+ using LengthBits = panda::BitField;
+ using BuiltinIdBits = LengthBits::NextField;
+ using IsConstructorBit = BuiltinIdBits::NextFlag;
+ using IsAccessorBit = IsConstructorBit::NextFlag;
+
+ template
+ static constexpr BuiltinFunctionEntry Create(std::string_view name, EcmaEntrypoint entrypoint,
+ int length, kungfu::BuiltinsStubCSigns::ID builtinId)
+ {
+ static_assert((std::is_same_v && ...),
+ "Only 1-bit fields are available in BitFieldArgs");
+ uint64_t bitfield = 0;
+ bitfield |= LengthBits::Encode(length);
+ bitfield |= BuiltinIdBits::Encode(builtinId);
+ // Traverses BitFieldArgs (IsConstructorBit, IsAccessorBit, etc.)
+ ((bitfield |= BitFieldArgs::Encode(true)), ...);
+ return BuiltinFunctionEntry(name, entrypoint, bitfield);
+ }
+
+ constexpr std::string_view GetName() const
+ {
+ return name_;
+ }
+
+ constexpr EcmaEntrypoint GetEntrypoint() const
+ {
+ return entrypoint_;
+ }
+
+ constexpr int GetLength() const
+ {
+ return LengthBits::Decode(bitfield_);
+ }
+
+ constexpr kungfu::BuiltinsStubCSigns::ID GetBuiltinStubId() const
+ {
+ return BuiltinIdBits::Decode(bitfield_);
+ }
+
+ constexpr bool IsConstructor() const
+ {
+ return IsConstructorBit::Decode(bitfield_);
+ }
+
+ constexpr bool IsAccessor() const
+ {
+ return IsAccessorBit::Decode(bitfield_);
+ }
+
+private:
+ std::string_view name_;
+ EcmaEntrypoint entrypoint_;
+ uint64_t bitfield_;
+
+ constexpr BuiltinFunctionEntry(std::string_view name, EcmaEntrypoint entrypoint, uint64_t bitfield)
+ : name_(name), entrypoint_(entrypoint), bitfield_(bitfield) {}
+};
+
class BuiltinsBase {
public:
enum ArgsPosition : uint32_t { FIRST = 0, SECOND, THIRD, FOURTH, FIFTH };
@@ -63,6 +153,11 @@ public:
return JSTaggedValue(value);
}
+ static inline JSTaggedValue GetTaggedInt64(int64_t value)
+ {
+ return JSTaggedValue(value);
+ }
+
static inline JSTaggedValue GetTaggedDouble(double value)
{
return JSTaggedValue(value);
diff --git a/ecmascript/base/config.h b/ecmascript/base/config.h
index 0dc57ae708d531f90a8d068366a75f103fc10793..0f9baeb51b3489697bd87cb10744b3777a505ae6 100644
--- a/ecmascript/base/config.h
+++ b/ecmascript/base/config.h
@@ -64,6 +64,15 @@ namespace panda::ecmascript {
#define ECMASCRIPT_ENABLE_HEAP_VERIFY 1
#define ECMASCRIPT_ENABLE_BARRIER_CHECK 1
#define ECMASCRIPT_ENABLE_NAPI_SPECIAL_CHECK 1
+#elif defined(ECMASCRIPT_ENABLE_DFX_CONFIG)
+ #define ECMASCRIPT_ENABLE_IC 1
+ #define ECMASCRIPT_ENABLE_ZAP_MEM 0
+ #define ECMASCRIPT_SWITCH_GC_MODE_TO_FULL_GC 0
+ #define ECMASCRIPT_ENABLE_CAST_CHECK 0
+ #define ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK 0
+ #define ECMASCRIPT_ENABLE_HEAP_VERIFY 1
+ #define ECMASCRIPT_ENABLE_BARRIER_CHECK 0
+ #define ECMASCRIPT_ENABLE_NAPI_SPECIAL_CHECK 1
#else
#define ECMASCRIPT_ENABLE_IC 1
#define ECMASCRIPT_ENABLE_ZAP_MEM 0
diff --git a/ecmascript/base/error_helper.cpp b/ecmascript/base/error_helper.cpp
index 78a0422f16d797efe39bff410f97d9565b904aa3..58197f6ac753f44874f67a3e74fb364636ddc48a 100644
--- a/ecmascript/base/error_helper.cpp
+++ b/ecmascript/base/error_helper.cpp
@@ -118,6 +118,9 @@ JSHandle ErrorHelper::GetErrorName(JSThread *thread, const JSHand
case ErrorType::OOM_ERROR:
errorKey = globalConst->GetHandledOOMErrorString();
break;
+ case ErrorType::TERMINATION_ERROR:
+ errorKey = globalConst->GetHandledTerminationErrorString();
+ break;
default:
errorKey = globalConst->GetHandledErrorString();
break;
@@ -128,7 +131,7 @@ JSHandle ErrorHelper::GetErrorName(JSThread *thread, const JSHand
}
JSTaggedValue ErrorHelper::ErrorCommonConstructor(EcmaRuntimeCallInfo *argv,
- [[maybe_unused]] const ErrorType &errorType)
+ const ErrorType &errorType)
{
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
@@ -166,7 +169,23 @@ JSTaggedValue ErrorHelper::ErrorCommonConstructor(EcmaRuntimeCallInfo *argv,
[[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, msgKey, msgDesc);
ASSERT_PRINT(status == true, "return result exception!");
}
-
+ // InstallErrorCause
+ JSHandle options = BuiltinsBase::GetCallArg(argv, 1);
+ // If options is an Object and ? HasProperty(options, "cause") is true, then
+ // a. Let cause be ? Get(options, "cause").
+ // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause).
+ if (options->IsECMAObject()) {
+ JSHandle causeKey = globalConst->GetHandledCauseString();
+ bool causePresent = JSTaggedValue::HasProperty(thread, options, causeKey);
+ RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
+ if (causePresent) {
+ JSHandle cause = JSObject::GetProperty(thread, options, causeKey).GetValue();
+ RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
+ PropertyDescriptor causeDesc(thread, cause, true, false, true);
+ [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, causeKey, causeDesc);
+ ASSERT_PRINT(status == true, "return result exception!");
+ }
+ }
JSHandle errorFunc = GetErrorJSFunction(thread);
if (!errorFunc->IsUndefined()) {
JSHandle errorFunckey = globalConst->GetHandledErrorFuncString();
@@ -176,7 +195,8 @@ JSTaggedValue ErrorHelper::ErrorCommonConstructor(EcmaRuntimeCallInfo *argv,
ASSERT_PRINT(status == true, "return result exception!");
}
- JSHandle handleStack = BuildEcmaStackTrace(thread);
+ bool isOOMError = errorType == ErrorType::OOM_ERROR;
+ JSHandle handleStack = BuildEcmaStackTrace(thread, isOOMError);
JSHandle stackkey = globalConst->GetHandledStackString();
PropertyDescriptor stackDesc(thread, JSHandle::Cast(handleStack), true, false, true);
[[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, stackkey, stackDesc);
@@ -205,9 +225,12 @@ JSHandle ErrorHelper::GetErrorJSFunction(JSThread *thread)
return thread->GlobalConstants()->GetHandledUndefined();
}
-JSHandle ErrorHelper::BuildEcmaStackTrace(JSThread *thread)
+JSHandle ErrorHelper::BuildEcmaStackTrace(JSThread *thread, bool isOOMError)
{
std::string data = JsStackInfo::BuildJsStackTrace(thread, false);
+ if (isOOMError) {
+ data = data.substr(0, MAX_ERROR_SIZE);
+ }
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
LOG_ECMA(DEBUG) << data;
return factory->NewFromStdString(data);
diff --git a/ecmascript/base/error_helper.h b/ecmascript/base/error_helper.h
index 5a1deeab3ade84bede2f86ff0bcbb16ea4c7d55b..aedf378e641f6844fa52921971da955da3aa5f7f 100644
--- a/ecmascript/base/error_helper.h
+++ b/ecmascript/base/error_helper.h
@@ -35,10 +35,11 @@ public:
private:
static JSHandle GetErrorJSFunction(JSThread *thread);
- static JSHandle BuildEcmaStackTrace(JSThread *thread);
+ static JSHandle BuildEcmaStackTrace(JSThread *thread, bool isOOMError);
static JSHandle GetErrorName(JSThread *thread, const JSHandle &name,
const ErrorType &errorType);
+ static constexpr uint32_t MAX_ERROR_SIZE = 128_KB;
};
} // namespace panda::ecmascript::base
diff --git a/ecmascript/base/error_type.h b/ecmascript/base/error_type.h
index 7905c0614b88a9fb96ab45f5f8a2e3478beb95ed..ce5acacf3c26c0810c980dd35b3a33f9440a032b 100644
--- a/ecmascript/base/error_type.h
+++ b/ecmascript/base/error_type.h
@@ -29,6 +29,7 @@ enum class ErrorType : uint8_t {
URI_ERROR,
AGGREGATE_ERROR,
OOM_ERROR,
+ TERMINATION_ERROR,
};
} // namespace panda::ecmascript::base
diff --git a/ecmascript/base/fast_json_stringifier.cpp b/ecmascript/base/fast_json_stringifier.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6a7cd607547205dfe090acd7bda72b4535cccfbc
--- /dev/null
+++ b/ecmascript/base/fast_json_stringifier.cpp
@@ -0,0 +1,939 @@
+/*
+ * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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/fast_json_stringifier.h"
+
+#include "ecmascript/base/builtins_base.h"
+#include "ecmascript/base/json_helper.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/global_dictionary-inl.h"
+#include "ecmascript/js_array.h"
+#include "ecmascript/js_function.h"
+#include "ecmascript/js_handle.h"
+#include "ecmascript/js_object-inl.h"
+#include "ecmascript/js_primitive_ref.h"
+#include "ecmascript/js_tagged_value-inl.h"
+#include "ecmascript/js_tagged_value.h"
+#include "ecmascript/object_fast_operator-inl.h"
+
+namespace panda::ecmascript::base {
+JSHandle FastJsonStringifier::Stringify(const JSHandle &value)
+{
+ factory_ = thread_->GetEcmaVM()->GetFactory();
+ JSHandle jsonCache = thread_->GetEcmaVM()->GetGlobalEnv()->GetJsonObjectHclassCache();
+ if (jsonCache->IsHole()) {
+ hclassCache_ = factory_->NewTaggedArray(JSON_CACHE_SIZE);
+ } else {
+ hclassCache_ = JSHandle::Cast(jsonCache);
+ }
+ JSTaggedValue tagValue = value.GetTaggedValue();
+ handleValue_ = JSMutableHandle(thread_, tagValue);
+ handleKey_ = JSMutableHandle(thread_, factory_->GetEmptyString());
+
+ if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
+ JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
+ RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
+ handleValue_.Update(serializeValue);
+ }
+
+ JSTaggedValue result = SerializeJSONProperty(handleValue_);
+
+ RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
+ if (!result.IsUndefined()) {
+ return JSHandle(
+ factory_->NewFromUtf8Literal(reinterpret_cast(result_.c_str()), result_.size()));
+ }
+ return thread_->GlobalConstants()->GetHandledUndefined();
+}
+
+JSTaggedValue FastJsonStringifier::GetSerializeValue(const JSHandle &key,
+ const JSHandle &value)
+{
+ JSTaggedValue tagValue = value.GetTaggedValue();
+ JSHandle undefined = thread_->GlobalConstants()->GetHandledUndefined();
+ // a. Let toJSON be Get(value, "toJSON").
+ JSHandle toJson = thread_->GlobalConstants()->GetHandledToJsonString();
+ JSHandle toJsonFun(
+ thread_, ObjectFastOperator::FastGetPropertyByValue(thread_, tagValue, toJson.GetTaggedValue()));
+ // b. ReturnIfAbrupt(toJSON).
+ RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
+ tagValue = value.GetTaggedValue();
+ // c. If IsCallable(toJSON) is true
+ if (UNLIKELY(toJsonFun->IsCallable())) {
+ // Let value be Call(toJSON, value, «key»).
+ EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread_, toJsonFun, value, undefined, 1);
+ RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
+ info->SetCallArg(key.GetTaggedValue());
+ tagValue = JSFunction::Call(info);
+ // ii. ReturnIfAbrupt(value).
+ RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
+ }
+ return tagValue;
+}
+
+JSTaggedValue FastJsonStringifier::SerializeJSONProperty(const JSHandle &value)
+{
+ JSTaggedValue tagValue = value.GetTaggedValue();
+ if (!tagValue.IsHeapObject()) {
+ JSTaggedType type = tagValue.GetRawData();
+ switch (type) {
+ // If value is false, return "false".
+ case JSTaggedValue::VALUE_FALSE:
+ result_ += "false";
+ return tagValue;
+ // If value is true, return "true".
+ case JSTaggedValue::VALUE_TRUE:
+ result_ += "true";
+ return tagValue;
+ // If value is null, return "null".
+ case JSTaggedValue::VALUE_NULL:
+ result_ += "null";
+ return tagValue;
+ default:
+ // If Type(value) is Number, then
+ if (tagValue.IsNumber()) {
+ // a. If value is finite, return ToString(value).
+ if (std::isfinite(tagValue.GetNumber())) {
+ result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, tagValue));
+ } else {
+ // b. Else, return "null".
+ result_ += "null";
+ }
+ return tagValue;
+ }
+ }
+ } else {
+ JSType jsType = tagValue.GetTaggedObject()->GetClass()->GetObjectType();
+ JSHandle valHandle(thread_, tagValue);
+ switch (jsType) {
+ case JSType::JS_ARRAY: {
+ SerializeJSArray(valHandle);
+ RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
+ return tagValue;
+ }
+ // If Type(value) is String, return QuoteJSONString(value).
+ case JSType::LINE_STRING:
+ case JSType::CONSTANT_STRING:
+ case JSType::TREE_STRING:
+ case JSType::SLICED_STRING: {
+ JSHandle strHandle = JSHandle(valHandle);
+ auto string = JSHandle(thread_,
+ EcmaStringAccessor::Flatten(thread_->GetEcmaVM(), strHandle));
+ CString str = ConvertToString(*string, StringConvertedUsage::LOGICOPERATION);
+ str = JsonHelper::ValueToQuotedString(str);
+ result_ += str;
+ return tagValue;
+ }
+ case JSType::JS_PRIMITIVE_REF: {
+ SerializePrimitiveRef(valHandle);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, JSTaggedValue::Exception());
+ return tagValue;
+ }
+ case JSType::SYMBOL:
+ return JSTaggedValue::Undefined();
+ case JSType::BIGINT: {
+ THROW_TYPE_ERROR_AND_RETURN(thread_, "cannot serialize a BigInt", JSTaggedValue::Exception());
+ }
+ default: {
+ if (!tagValue.IsCallable()) {
+ JSHClass *jsHclass = tagValue.GetTaggedObject()->GetClass();
+ if (UNLIKELY(jsHclass->IsJSProxy() &&
+ JSProxy::Cast(tagValue.GetTaggedObject())->IsArray(thread_))) {
+ SerializeJSProxy(valHandle);
+ RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
+ } else {
+ SerializeJSONObject(valHandle);
+ RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
+ }
+ return tagValue;
+ }
+ }
+ }
+ }
+ return JSTaggedValue::Undefined();
+}
+
+CString FastJsonStringifier::SerializeObjectKey(const JSHandle &key, bool hasContent)
+{
+ if (hasContent) {
+ result_ += ",";
+ }
+
+ CString str;
+ if (key->IsString()) {
+ str = ConvertToString(EcmaString::Cast(key->GetTaggedObject()), StringConvertedUsage::LOGICOPERATION);
+ } else if (key->IsInt()) {
+ str = NumberHelper::IntToString(static_cast(key->GetInt()));
+ } else {
+ str = ConvertToString(*JSTaggedValue::ToString(thread_, key), StringConvertedUsage::LOGICOPERATION);
+ }
+ str = JsonHelper::ValueToQuotedString(str);
+ result_ += str;
+ result_ += ":";
+
+ return str;
+}
+
+bool FastJsonStringifier::PushValue(const JSHandle &value)
+{
+ uint32_t thisLen = stack_.size();
+
+ for (uint32_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 FastJsonStringifier::PopValue()
+{
+ stack_.pop_back();
+}
+
+bool FastJsonStringifier::SerializeJSONObject(const JSHandle &value)
+{
+ bool isContain = PushValue(value);
+ if (isContain) {
+ THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
+ }
+
+ result_ += "{";
+ bool hasContent = false;
+
+ ASSERT(!value->IsAccessor());
+ JSHandle obj(value);
+ if (UNLIKELY(value->IsJSProxy() || value->IsTypedArray())) { // serialize proxy and typedArray
+ JSHandle propertyArray = JSObject::EnumerableOwnNames(thread_, obj);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ uint32_t arrLength = propertyArray->GetLength();
+ for (uint32_t i = 0; i < arrLength; i++) {
+ handleKey_.Update(propertyArray->Get(i));
+ JSHandle valueHandle = JSTaggedValue::GetProperty(thread_, value, handleKey_).GetValue();
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (UNLIKELY(valueHandle->IsECMAObject() || valueHandle->IsBigInt())) {
+ JSTaggedValue serializeValue = GetSerializeValue(handleKey_, valueHandle);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
+ (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
+ continue;
+ }
+ handleValue_.Update(serializeValue);
+ } else {
+ handleValue_.Update(valueHandle);
+ }
+ SerializeObjectKey(handleKey_, hasContent);
+ JSTaggedValue res = SerializeJSONProperty(handleValue_);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (!res.IsUndefined()) {
+ hasContent = true;
+ }
+ }
+ } else {
+ uint32_t numOfKeys = obj->GetNumberOfKeys();
+ uint32_t numOfElements = obj->GetNumberOfElements();
+ if (numOfKeys + numOfElements < CACHE_MINIMUN_SIZIE || !cacheable_) {
+ if (numOfElements > 0) {
+ hasContent = DefaultSerializeElements(obj, hasContent);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ if (numOfKeys > 0) {
+ hasContent = DefaultSerializeKeys(obj, hasContent);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ } else {
+ JSHClass *jsHclass = value->GetTaggedObject()->GetClass();
+ int32_t index = FindCache(jsHclass, numOfKeys + numOfElements);
+ if (index != INVALID_INDEX) {
+ auto strCache = thread_->GetCurrentEcmaContext()->GetJsonStringifyCache(index);
+ uint32_t cacheIndex = 0;
+ if (numOfElements > 0) {
+ hasContent = SerializeElementsWithCache(obj, hasContent, strCache, cacheIndex, numOfElements);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ if (numOfKeys > 0) {
+ hasContent = SerializeKeysWithCache(obj, hasContent, strCache, cacheIndex);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ } else {
+ CVector> strCache;
+ if (numOfElements > 0) {
+ hasContent = TryCacheSerializeElements(obj, hasContent, strCache);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ if (numOfKeys > 0) {
+ hasContent = TryCacheSerializeKeys(obj, hasContent, strCache);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ if (cacheable_) {
+ SetCache(value->GetTaggedObject()->GetClass(), numOfElements + numOfKeys, strCache);
+ }
+ }
+ }
+ }
+
+ result_ += "}";
+ PopValue();
+ return true;
+}
+
+bool FastJsonStringifier::SerializeJSProxy(const JSHandle &object)
+{
+ bool isContain = PushValue(object);
+ if (isContain) {
+ THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
+ }
+
+ result_ += "[";
+ JSHandle proxy(object);
+ JSHandle lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
+ JSHandle lenghHandle = JSProxy::GetProperty(thread_, proxy, lengthKey).GetValue();
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenghHandle);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ uint32_t length = lenNumber.ToUint32();
+ for (uint32_t i = 0; i < length; i++) {
+ handleKey_.Update(JSTaggedValue(i));
+ JSHandle valHandle = JSProxy::GetProperty(thread_, proxy, handleKey_).GetValue();
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (i > 0) {
+ result_ += ",";
+ }
+ if (UNLIKELY(valHandle->IsECMAObject() || valHandle->IsBigInt())) {
+ JSTaggedValue serializeValue = GetSerializeValue(handleKey_, valHandle);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ handleValue_.Update(serializeValue);
+ } else {
+ handleValue_.Update(valHandle);
+ }
+ JSTaggedValue res = SerializeJSONProperty(handleValue_);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (res.IsUndefined()) {
+ result_ += "null";
+ }
+ }
+
+ result_ += "]";
+ PopValue();
+ return true;
+}
+
+bool FastJsonStringifier::SerializeJSArray(const JSHandle &value)
+{
+ // 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);
+ }
+
+ result_ += "[";
+ JSHandle jsArr(value);
+ uint32_t len = jsArr->GetArrayLength();
+ if (len > 0) {
+ for (uint32_t i = 0; i < len; i++) {
+ JSTaggedValue tagVal = ObjectFastOperator::FastGetPropertyByIndex(thread_, value.GetTaggedValue(), i);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (UNLIKELY(tagVal.IsAccessor())) {
+ tagVal = JSObject::CallGetter(thread_, AccessorData::Cast(tagVal.GetTaggedObject()), value);
+ }
+ handleKey_.Update(JSTaggedValue(i));
+ handleValue_.Update(tagVal);
+
+ if (i > 0) {
+ result_ += ",";
+ }
+ if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
+ JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ handleValue_.Update(serializeValue);
+ }
+ JSTaggedValue res = SerializeJSONProperty(handleValue_);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (res.IsUndefined()) {
+ result_ += "null";
+ }
+ }
+ }
+
+ result_ += "]";
+ PopValue();
+ return true;
+}
+
+void FastJsonStringifier::SerializePrimitiveRef(const JSHandle &primitiveRef)
+{
+ JSTaggedValue primitive = JSPrimitiveRef::Cast(primitiveRef.GetTaggedValue().GetTaggedObject())->GetValue();
+ if (primitive.IsString()) {
+ auto priStr = JSTaggedValue::ToString(thread_, primitiveRef);
+ RETURN_IF_ABRUPT_COMPLETION(thread_);
+ CString str = ConvertToString(*priStr, StringConvertedUsage::LOGICOPERATION);
+ str = JsonHelper::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";
+ } else if (primitive.IsBigInt()) {
+ THROW_TYPE_ERROR(thread_, "cannot serialize a BigInt");
+ }
+}
+
+bool FastJsonStringifier::TryCacheSerializeElements(const JSHandle &obj, bool hasContent,
+ CVector> &strCache)
+{
+ JSHandle elementsArr(thread_, obj->GetElements());
+ if (!elementsArr->IsDictionaryMode()) {
+ uint32_t elementsLen = elementsArr->GetLength();
+ for (uint32_t i = 0; i < elementsLen; ++i) {
+ if (!elementsArr->Get(i).IsHole()) {
+ handleKey_.Update(JSTaggedValue(i));
+ handleValue_.Update(elementsArr->Get(i));
+ hasContent = AppendJsonString(hasContent, strCache, i);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ }
+ } else {
+ JSHandle numberDic(elementsArr);
+ CVector> sortArr;
+ int size = numberDic->Size();
+ for (int hashIndex = 0; hashIndex < size; hashIndex++) {
+ JSTaggedValue key = numberDic->GetKey(hashIndex);
+ if (!key.IsUndefined() && !key.IsHole()) {
+ PropertyAttributes attr = numberDic->GetAttributes(hashIndex);
+ if (attr.IsEnumerable()) {
+ JSTaggedValue numberKey = JSTaggedValue(static_cast(key.GetInt()));
+ sortArr.emplace_back(JSHandle(thread_, numberKey));
+ }
+ }
+ }
+ std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareNumber);
+ for (const auto &entry : sortArr) {
+ JSTaggedValue entryKey = entry.GetTaggedValue();
+ handleKey_.Update(entryKey);
+ int index = numberDic->FindEntry(entryKey);
+ JSTaggedValue value = numberDic->GetValue(index);
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = AppendJsonString(hasContent, strCache, index);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ }
+ return hasContent;
+}
+
+bool FastJsonStringifier::SerializeElementsWithCache(const JSHandle &obj, bool hasContent,
+ CVector> &strCache, uint32_t &cacheIndex, uint32_t elementSize)
+{
+ JSHandle elementsArr(thread_, obj->GetElements());
+ if (!elementsArr->IsDictionaryMode()) {
+ uint32_t elementsLen = elementsArr->GetLength();
+ for (uint32_t i = 0; i < elementsLen; ++i) {
+ if (!elementsArr->Get(i).IsHole()) {
+ CString key = strCache[cacheIndex++].first;
+ handleValue_.Update(elementsArr->Get(i));
+ hasContent = FastAppendJsonString(hasContent, key);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ }
+ } else {
+ JSHandle numberDic(elementsArr);
+ for (; cacheIndex < elementSize; cacheIndex++) {
+ CString key = strCache[cacheIndex].first;
+ int index = strCache[cacheIndex].second;
+ JSTaggedValue value = numberDic->GetValue(index);
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = FastAppendJsonString(hasContent, key);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ }
+ return hasContent;
+}
+
+bool FastJsonStringifier::TryCacheSerializeKeys(const JSHandle &obj, bool hasContent,
+ CVector> &strCache)
+{
+ JSHandle propertiesArr(thread_, obj->GetProperties());
+ if (!propertiesArr->IsDictionaryMode()) {
+ JSHandle jsHclass(thread_, obj->GetJSHClass());
+ JSTaggedValue enumCache = jsHclass->GetEnumCache();
+ if (JSObject::GetEnumCacheKind(thread_, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) {
+ JSHandle cache(thread_, enumCache);
+ uint32_t length = cache->GetLength();
+ for (uint32_t i = 0; i < length; i++) {
+ JSTaggedValue key = cache->Get(i);
+ if (!key.IsString()) {
+ continue;
+ }
+ handleKey_.Update(key);
+ JSTaggedValue value;
+ LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
+ int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
+ PropertyAttributes attr(layoutInfo->GetAttr(index));
+ ASSERT(static_cast(attr.GetOffset()) == index);
+ value = attr.IsInlinedProps()
+ ? obj->GetPropertyInlinedPropsWithRep(static_cast(index), attr)
+ : propertiesArr->Get(static_cast(index) - jsHclass->GetInlinedProperties());
+ if (attr.IsInlinedProps() && value.IsHole()) {
+ continue;
+ }
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = AppendJsonString(hasContent, strCache, index);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ return hasContent;
+ }
+ int end = static_cast(jsHclass->NumberOfProps());
+ if (end <= 0) {
+ return hasContent;
+ }
+ for (int i = 0; i < end; i++) {
+ LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
+ JSTaggedValue key = layoutInfo->GetKey(i);
+ if (key.IsString() && layoutInfo->GetAttr(i).IsEnumerable()) {
+ handleKey_.Update(key);
+ JSTaggedValue value;
+ int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
+ PropertyAttributes attr(layoutInfo->GetAttr(index));
+ ASSERT(static_cast(attr.GetOffset()) == index);
+ value = attr.IsInlinedProps()
+ ? obj->GetPropertyInlinedPropsWithRep(static_cast(index), attr)
+ : propertiesArr->Get(static_cast(index) - jsHclass->GetInlinedProperties());
+ if (attr.IsInlinedProps() && value.IsHole()) {
+ continue;
+ }
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = AppendJsonString(hasContent, strCache, index);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ }
+ return hasContent;
+ }
+ if (obj->IsJSGlobalObject()) {
+ JSHandle globalDic(propertiesArr);
+ int size = globalDic->Size();
+ CVector, PropertyAttributes>> sortArr;
+ for (int hashIndex = 0; hashIndex < size; hashIndex++) {
+ JSTaggedValue key = globalDic->GetKey(hashIndex);
+ if (!key.IsString()) {
+ continue;
+ }
+ PropertyAttributes attr = globalDic->GetAttributes(hashIndex);
+ if (!attr.IsEnumerable()) {
+ continue;
+ }
+ std::pair, PropertyAttributes> pair(JSHandle(thread_, key), attr);
+ sortArr.emplace_back(pair);
+ }
+ std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
+ for (const auto &entry : sortArr) {
+ JSTaggedValue entryKey = entry.first.GetTaggedValue();
+ handleKey_.Update(entryKey);
+ int index = globalDic->FindEntry(entryKey);
+ JSTaggedValue value = globalDic->GetValue(index);
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = AppendJsonString(hasContent, strCache, index);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ return hasContent;
+ }
+ JSHandle nameDic(propertiesArr);
+ int size = nameDic->Size();
+ CVector, 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, PropertyAttributes> pair(JSHandle(thread_, key), attr);
+ sortArr.emplace_back(pair);
+ }
+ std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
+ for (const auto &entry : sortArr) {
+ JSTaggedValue entryKey = entry.first.GetTaggedValue();
+ handleKey_.Update(entryKey);
+ int index = nameDic->FindEntry(entryKey);
+ JSTaggedValue value = nameDic->GetValue(index);
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = AppendJsonString(hasContent, strCache, index);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ return hasContent;
+}
+
+bool FastJsonStringifier::SerializeKeysWithCache(const JSHandle &obj, bool hasContent,
+ CVector> &strCache, uint32_t &cacheIndex)
+{
+ JSHandle jsHclass(thread_, obj->GetJSHClass());
+ JSHandle propertiesArr(thread_, obj->GetProperties());
+ if (!propertiesArr->IsDictionaryMode()) {
+ for (; cacheIndex < strCache.size(); cacheIndex++) {
+ auto cacheValue = strCache[cacheIndex];
+ CString str = cacheValue.first;
+ int index = cacheValue.second;
+ LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
+ PropertyAttributes attr(layoutInfo->GetAttr(index));
+ JSTaggedValue value = attr.IsInlinedProps()
+ ? obj->GetPropertyInlinedPropsWithRep(static_cast(index), attr)
+ : propertiesArr->Get(static_cast(index) - jsHclass->GetInlinedProperties());
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = FastAppendJsonString(hasContent, str);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ return hasContent;
+ }
+ if (obj->IsJSGlobalObject()) {
+ JSHandle globalDic(propertiesArr);
+ for (; cacheIndex < strCache.size(); cacheIndex++) {
+ auto cacheValue = strCache[cacheIndex];
+ CString str = cacheValue.first;
+ int index = cacheValue.second;
+ JSTaggedValue value = globalDic->GetValue(index);
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = FastAppendJsonString(hasContent, str);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ return hasContent;
+ }
+ JSHandle nameDic(propertiesArr);
+ for (; cacheIndex < strCache.size(); cacheIndex++) {
+ auto cacheValue = strCache[cacheIndex];
+ CString str = cacheValue.first;
+ int index = cacheValue.second;
+ JSTaggedValue value = nameDic->GetValue(index);
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = FastAppendJsonString(hasContent, str);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ return hasContent;
+}
+
+bool FastJsonStringifier::AppendJsonString(bool hasContent, CVector> &strCache, int index)
+{
+ if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
+ JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
+ (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
+ return hasContent;
+ }
+ handleValue_.Update(serializeValue);
+ }
+ CString keyStr = SerializeObjectKey(handleKey_, hasContent);
+ strCache.emplace_back(std::pair(keyStr, index));
+ JSTaggedValue res = SerializeJSONProperty(handleValue_);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (!res.IsUndefined()) {
+ return true;
+ }
+ EraseKeyString(keyStr, hasContent);
+ return hasContent;
+}
+
+bool FastJsonStringifier::FastAppendJsonString(bool hasContent, CString &key)
+{
+ if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
+ JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
+ (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
+ return hasContent;
+ }
+ handleValue_.Update(serializeValue);
+ }
+ FastSerializeObjectKey(key, hasContent);
+ JSTaggedValue res = SerializeJSONProperty(handleValue_);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (!res.IsUndefined()) {
+ return true;
+ }
+ EraseKeyString(key, hasContent);
+ return hasContent;
+}
+
+bool FastJsonStringifier::DefaultSerializeElements(const JSHandle &obj, bool hasContent)
+{
+ JSHandle elementsArr(thread_, obj->GetElements());
+ if (!elementsArr->IsDictionaryMode()) {
+ uint32_t elementsLen = elementsArr->GetLength();
+ for (uint32_t i = 0; i < elementsLen; ++i) {
+ if (!elementsArr->Get(i).IsHole()) {
+ handleKey_.Update(JSTaggedValue(i));
+ handleValue_.Update(elementsArr->Get(i));
+ hasContent = AppendJsonString(hasContent);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ }
+ } else {
+ JSHandle numberDic(elementsArr);
+ CVector> sortArr;
+ int size = numberDic->Size();
+ for (int hashIndex = 0; hashIndex < size; hashIndex++) {
+ JSTaggedValue key = numberDic->GetKey(hashIndex);
+ if (!key.IsUndefined() && !key.IsHole()) {
+ PropertyAttributes attr = numberDic->GetAttributes(hashIndex);
+ if (attr.IsEnumerable()) {
+ JSTaggedValue numberKey = JSTaggedValue(static_cast(key.GetInt()));
+ sortArr.emplace_back(JSHandle(thread_, numberKey));
+ }
+ }
+ }
+ std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareNumber);
+ for (const auto &entry : sortArr) {
+ JSTaggedValue entryKey = entry.GetTaggedValue();
+ handleKey_.Update(entryKey);
+ int index = numberDic->FindEntry(entryKey);
+ JSTaggedValue value = numberDic->GetValue(index);
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = AppendJsonString(hasContent);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ }
+ return hasContent;
+}
+
+bool FastJsonStringifier::DefaultSerializeKeys(const JSHandle &obj, bool hasContent)
+{
+ JSHandle propertiesArr(thread_, obj->GetProperties());
+ if (!propertiesArr->IsDictionaryMode()) {
+ JSHandle jsHclass(thread_, obj->GetJSHClass());
+ JSTaggedValue enumCache = jsHclass->GetEnumCache();
+ if (JSObject::GetEnumCacheKind(thread_, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) {
+ JSHandle cache(thread_, enumCache);
+ uint32_t length = cache->GetLength();
+ for (uint32_t i = 0; i < length; i++) {
+ JSTaggedValue key = cache->Get(i);
+ if (!key.IsString()) {
+ continue;
+ }
+ handleKey_.Update(key);
+ JSTaggedValue value;
+ LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
+ int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
+ PropertyAttributes attr(layoutInfo->GetAttr(index));
+ ASSERT(static_cast(attr.GetOffset()) == index);
+ value = attr.IsInlinedProps()
+ ? obj->GetPropertyInlinedPropsWithRep(static_cast(index), attr)
+ : propertiesArr->Get(static_cast(index) - jsHclass->GetInlinedProperties());
+ if (attr.IsInlinedProps() && value.IsHole()) {
+ continue;
+ }
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = AppendJsonString(hasContent);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ return hasContent;
+ }
+ int end = static_cast(jsHclass->NumberOfProps());
+ if (end <= 0) {
+ return hasContent;
+ }
+ for (int i = 0; i < end; i++) {
+ LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
+ JSTaggedValue key = layoutInfo->GetKey(i);
+ if (key.IsString() && layoutInfo->GetAttr(i).IsEnumerable()) {
+ handleKey_.Update(key);
+ JSTaggedValue value;
+ int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
+ PropertyAttributes attr(layoutInfo->GetAttr(index));
+ ASSERT(static_cast(attr.GetOffset()) == index);
+ value = attr.IsInlinedProps()
+ ? obj->GetPropertyInlinedPropsWithRep(static_cast(index), attr)
+ : propertiesArr->Get(static_cast(index) - jsHclass->GetInlinedProperties());
+ if (attr.IsInlinedProps() && value.IsHole()) {
+ continue;
+ }
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = AppendJsonString(hasContent);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ }
+ return hasContent;
+ }
+ if (obj->IsJSGlobalObject()) {
+ JSHandle globalDic(propertiesArr);
+ int size = globalDic->Size();
+ CVector, PropertyAttributes>> sortArr;
+ for (int hashIndex = 0; hashIndex < size; hashIndex++) {
+ JSTaggedValue key = globalDic->GetKey(hashIndex);
+ if (!key.IsString()) {
+ continue;
+ }
+ PropertyAttributes attr = globalDic->GetAttributes(hashIndex);
+ if (!attr.IsEnumerable()) {
+ continue;
+ }
+ std::pair, PropertyAttributes> pair(JSHandle(thread_, key), attr);
+ sortArr.emplace_back(pair);
+ }
+ std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
+ for (const auto &entry : sortArr) {
+ JSTaggedValue entryKey = entry.first.GetTaggedValue();
+ handleKey_.Update(entryKey);
+ int index = globalDic->FindEntry(entryKey);
+ JSTaggedValue value = globalDic->GetValue(index);
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = AppendJsonString(hasContent);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ return hasContent;
+ }
+ JSHandle nameDic(propertiesArr);
+ int size = nameDic->Size();
+ CVector, 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, PropertyAttributes> pair(JSHandle(thread_, key), attr);
+ sortArr.emplace_back(pair);
+ }
+ std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
+ for (const auto &entry : sortArr) {
+ JSTaggedValue entryKey = entry.first.GetTaggedValue();
+ handleKey_.Update(entryKey);
+ int index = nameDic->FindEntry(entryKey);
+ JSTaggedValue value = nameDic->GetValue(index);
+ if (UNLIKELY(value.IsAccessor())) {
+ value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
+ JSHandle(obj));
+ }
+ handleValue_.Update(value);
+ hasContent = AppendJsonString(hasContent);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ return hasContent;
+}
+
+bool FastJsonStringifier::AppendJsonString(bool hasContent)
+{
+ if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
+ JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
+ (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
+ return hasContent;
+ }
+ handleValue_.Update(serializeValue);
+ }
+ CString keyStr = SerializeObjectKey(handleKey_, hasContent);
+ JSTaggedValue res = SerializeJSONProperty(handleValue_);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ if (!res.IsUndefined()) {
+ return true;
+ }
+ EraseKeyString(keyStr, hasContent);
+ return hasContent;
+}
+
+bool FastJsonStringifier::DefaultSerializeObject(const JSTaggedValue &object, uint32_t numOfKeys,
+ uint32_t numOfElements)
+{
+ JSHandle value(thread_, object);
+ bool isContain = PushValue(value);
+ if (isContain) {
+ THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
+ }
+
+ result_ += "{";
+ bool hasContent = false;
+
+ JSHandle obj(value);
+ if (numOfElements > 0) {
+ hasContent = DefaultSerializeElements(obj, hasContent);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+ if (numOfKeys > 0) {
+ hasContent = DefaultSerializeKeys(obj, hasContent);
+ RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
+ }
+
+ result_ += "}";
+ PopValue();
+ return true;
+}
+} // namespace panda::ecmascript::base
diff --git a/ecmascript/base/fast_json_stringifier.h b/ecmascript/base/fast_json_stringifier.h
new file mode 100644
index 0000000000000000000000000000000000000000..32528ed402fc4d81e486f9917e96e7417196514d
--- /dev/null
+++ b/ecmascript/base/fast_json_stringifier.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ECMASCRIPT_BASE_FAST_JSON_STRINGIFY_H
+#define ECMASCRIPT_BASE_FAST_JSON_STRINGIFY_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 FastJsonStringifier {
+public:
+ static constexpr int32_t INVALID_INDEX = -1;
+ static constexpr int32_t JSON_CACHE_MASK = 62;
+ static constexpr int32_t JSON_CACHE_SIZE = 64;
+ static constexpr int32_t CACHE_MINIMUN_SIZIE = 5;
+ FastJsonStringifier() = default;
+
+ explicit FastJsonStringifier(JSThread *thread) : thread_(thread) {}
+
+ ~FastJsonStringifier() = default;
+ NO_COPY_SEMANTIC(FastJsonStringifier);
+ NO_MOVE_SEMANTIC(FastJsonStringifier);
+
+ JSHandle Stringify(const JSHandle &value);
+
+private:
+ JSTaggedValue SerializeJSONProperty(const JSHandle &value);
+ JSTaggedValue GetSerializeValue(const JSHandle &key, const JSHandle &value);
+ CString SerializeObjectKey(const JSHandle &key, bool hasContent);
+
+ bool SerializeJSONObject(const JSHandle &value);
+
+ bool SerializeJSArray(const JSHandle &value);
+ bool SerializeJSProxy(const JSHandle &object);
+
+ void SerializePrimitiveRef(const JSHandle &primitiveRef);
+
+ bool PushValue(const JSHandle &value);
+
+ void PopValue();
+
+ bool AppendJsonString(bool hasContent, CVector> &strCache, int index);
+ bool FastAppendJsonString(bool hasContent, CString &key);
+ bool TryCacheSerializeElements(const JSHandle &obj, bool hasContent,
+ CVector> &strCache);
+ bool SerializeElementsWithCache(const JSHandle &obj, bool hasContent,
+ CVector> &strCache, uint32_t &cacheIndex,
+ uint32_t elementSize);
+ bool TryCacheSerializeKeys(const JSHandle &obj, bool hasContent,
+ CVector> &strCache);
+ bool SerializeKeysWithCache(const JSHandle &obj, bool hasContent,
+ CVector> &strCache, uint32_t &cacheIndex);
+ bool AppendJsonString(bool hasContent);
+ bool DefaultSerializeKeys(const JSHandle &obj, bool hasContent);
+ bool DefaultSerializeElements(const JSHandle &obj, bool hasContent);
+ bool DefaultSerializeObject(const JSTaggedValue &object, uint32_t numOfKeys, uint32_t numOfElements);
+
+ inline void EraseKeyString(CString &keyStr, bool hasContent)
+ {
+ size_t keyLength = keyStr.length() + (hasContent ? 1 : 0) + 1;
+ result_.erase(result_.end() - keyLength, result_.end());
+ }
+
+ inline void FastSerializeObjectKey(CString &key, bool hasContent)
+ {
+ if (hasContent) {
+ result_ += ",";
+ }
+
+ result_ += key;
+ result_ += ":";
+ }
+
+ inline int32_t FindCache(JSHClass *hclass, size_t numOfKeys)
+ {
+ size_t index = GetHash(hclass, numOfKeys);
+ JSTaggedValue cacheHclass = hclassCache_->Get(index);
+ if (cacheHclass != JSTaggedValue::Hole()) {
+ if (JSHClass::Cast(cacheHclass.GetTaggedObject()) == hclass) {
+ return index;
+ } else {
+ cacheHclass = hclassCache_->Get(++index);
+ if (JSHClass::Cast(cacheHclass.GetTaggedObject()) == hclass) {
+ return index;
+ } else {
+ return INVALID_INDEX;
+ }
+ }
+ }
+ return INVALID_INDEX;
+ }
+
+ inline void SetCache(JSHClass *hclass, size_t numOfKeys, CVector> &value)
+ {
+ size_t index = GetHash(hclass, numOfKeys);
+ JSTaggedValue cacheHclass = hclassCache_->Get(index);
+ if (cacheHclass != JSTaggedValue::Hole()) {
+ cacheHclass = hclassCache_->Get(++index);
+ if (cacheHclass != JSTaggedValue::Hole()) {
+ --index;
+ }
+ }
+ hclassCache_->Set(thread_, index, JSTaggedValue(hclass));
+ thread_->GetCurrentEcmaContext()->SetJsonStringifyCache(index, value);
+ }
+
+ inline size_t GetHash(JSHClass *hclass, size_t numOfKeys)
+ {
+ uintptr_t ptr = reinterpret_cast(hclass);
+ size_t hash = (ptr + numOfKeys) & JSON_CACHE_MASK;
+ return hash;
+ }
+
+ CString result_;
+ JSThread *thread_ {nullptr};
+ ObjectFactory *factory_ {nullptr};
+ CVector> stack_;
+ JSMutableHandle handleKey_ {};
+ JSMutableHandle handleValue_ {};
+ bool cacheable_ {true};
+ JSHandle hclassCache_ {};
+};
+} // namespace panda::ecmascript::basekey
+#endif // ECMASCRIPT_BASE_FAST_JSON_STRINGIFY_H
diff --git a/ecmascript/base/file_header.h b/ecmascript/base/file_header.h
index a34ce20ead1f27bbd9926656135e228b97d2961e..1d70e938419f522e5834eed87c6d05d13ca1100d 100644
--- a/ecmascript/base/file_header.h
+++ b/ecmascript/base/file_header.h
@@ -19,15 +19,18 @@
#include "ecmascript/base/string_helper.h"
#include "ecmascript/log_wrapper.h"
#include "utils/bit_utils.h"
+#include "zlib.h"
+
#include
#include
#include
namespace panda::ecmascript::base {
-class FileHeader {
+class FileHeaderBase {
public:
static constexpr size_t MAGIC_SIZE = 8;
static constexpr size_t VERSION_SIZE = 4;
+ static constexpr uint32_t CHECKSUM_END_OFFSET = MAGIC_SIZE + VERSION_SIZE + sizeof(uint32_t);
static constexpr std::array MAGIC = {'P', 'A', 'N', 'D', 'A', '\0', '\0', '\0'};
using VersionType = std::array;
@@ -59,23 +62,7 @@ public:
return ret;
}
-protected:
- explicit FileHeader(const VersionType &lastVersion) : magic_(MAGIC), version_(lastVersion) {}
-
- static bool VerifyVersion(const char *fileDesc, const VersionType &currVersion, const VersionType &lastVersion,
- bool strictMatch)
- {
- bool matched = strictMatch ? currVersion == lastVersion : currVersion <= lastVersion;
- if (!matched) {
- LOG_HOST_TOOL_ERROR << fileDesc << " version error, expected version should be "
- << (strictMatch ? "equal to " : "less or equal than ") << ConvToStr(lastVersion)
- << ", but got " << ConvToStr(currVersion);
- return false;
- }
- return true;
- }
-
- bool InternalVerify(const char *fileDesc, const VersionType &lastVersion, bool strictMatch) const
+ bool VerifyVersion(const char *fileDesc, const VersionType &lastVersion, bool strictMatch) const
{
if (magic_ != MAGIC) {
LOG_HOST_TOOL_ERROR << "Magic mismatch, please make sure " << fileDesc
@@ -91,11 +78,37 @@ protected:
return true;
}
- bool InternalVerifyVersion(const VersionType &expectVersion) const
+ bool CompatibleVerify(const VersionType &expectVersion) const
{
return version_ >= expectVersion;
}
+ VersionType GetVersion() const
+ {
+ return version_;
+ }
+
+ void SetVersion(VersionType version)
+ {
+ version_ = version;
+ }
+
+protected:
+ explicit FileHeaderBase(const VersionType &lastVersion) : magic_(MAGIC), version_(lastVersion) {}
+
+ static bool VerifyVersion(const char *fileDesc, const VersionType &currVersion, const VersionType &lastVersion,
+ bool strictMatch)
+ {
+ bool matched = strictMatch ? (currVersion == lastVersion) : (currVersion <= lastVersion);
+ if (!matched) {
+ LOG_HOST_TOOL_ERROR << fileDesc << " version error, expected version should be "
+ << (strictMatch ? "equal to " : "less or equal than ") << ConvToStr(lastVersion)
+ << ", but got " << ConvToStr(currVersion);
+ return false;
+ }
+ return true;
+ }
+
std::string InternalGetVersion() const
{
return ConvToStr(version_);
@@ -124,5 +137,52 @@ private:
VersionType version_;
};
+class FileHeaderElastic : public FileHeaderBase {
+public:
+ static constexpr uint32_t ENDIAN_VALUE = 0x12345678;
+ void SetChecksum(uint32_t checksum)
+ {
+ checksum_ = checksum;
+ }
+
+ uint32_t GetChecksum() const
+ {
+ return checksum_;
+ }
+
+ void SetHeaderSize(uint32_t size)
+ {
+ headerSize_ = size;
+ }
+
+ uint32_t GetHeaderSize() const
+ {
+ return headerSize_;
+ }
+
+ void SetFileSize(uint32_t size)
+ {
+ fileSize_ = size;
+ }
+
+ uint32_t GetFileSize() const
+ {
+ return fileSize_;
+ }
+
+ uint32_t GetEndianTag() const
+ {
+ return endianTag_;
+ }
+
+protected:
+ explicit FileHeaderElastic(const VersionType &lastVersion) : FileHeaderBase(lastVersion) {}
+
+private:
+ uint32_t checksum_ {0};
+ uint32_t fileSize_ {0};
+ uint32_t headerSize_ {0};
+ uint32_t endianTag_ {ENDIAN_VALUE};
+};
} // namespace panda::ecmascript::base
#endif // ECMASCRIPT_BASE_FILE_HEADER_H
diff --git a/ecmascript/base/json_helper.cpp b/ecmascript/base/json_helper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c0fb8de8bd83dc65c3d352c95bfd24d0ced7449d
--- /dev/null
+++ b/ecmascript/base/json_helper.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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_helper.h"
+
+#include
+#include
+#include
+
+namespace panda::ecmascript::base {
+constexpr unsigned char CODE_SPACE = 0x20;
+constexpr int FOUR_HEX = 4;
+constexpr char ZERO_FIRST = static_cast(0xc0); // \u0000 => c0 80
+
+bool JsonHelper::IsFastValueToQuotedString(const char *value)
+{
+ if (strpbrk(value, "\"\\\b\f\n\r\t") != nullptr) {
+ return false;
+ }
+ while (*value != '\0') {
+ if ((*value > 0 && *value < CODE_SPACE) || *value == ZERO_FIRST) {
+ return false;
+ }
+ value++;
+ }
+ return true;
+}
+
+CString JsonHelper::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;
+ case ZERO_FIRST:
+ product += "\\u0000";
+ ++c;
+ break;
+ default:
+ // c. Else if C has a code unit value less than 0x0020 (SPACE), then
+ if (*c > 0 && *c < CODE_SPACE) {
+ /*
+ * i. Let product be the concatenation of product and code unit 0x005C (REVERSE SOLIDUS).
+ * ii. Let product be the concatenation of product and "u".
+ * iii. Let hex be the string result of converting the numeric code unit value of C to a String of
+ * four hexadecimal digits. Alphabetic hexadecimal digits are presented as lowercase Latin letters.
+ * iv. Let product be the concatenation of product and hex.
+ */
+ std::ostringstream oss;
+ oss << "\\u" << std::hex << std::setfill('0') << std::setw(FOUR_HEX) << static_cast(*c);
+ product += oss.str();
+ } else {
+ // Else,
+ // i. Let product be the concatenation of product and C.
+ product += *c;
+ }
+ }
+ }
+ // 3. Let product be the concatenation of product and code unit 0x0022 (QUOTATION MARK).
+ product += "\"";
+ // Return product.
+ return product;
+}
+} // namespace panda::ecmascript::base
\ No newline at end of file
diff --git a/ecmascript/base/json_helper.h b/ecmascript/base/json_helper.h
new file mode 100644
index 0000000000000000000000000000000000000000..7cd093f9c5c82a50df600704058b09af1e2e5e34
--- /dev/null
+++ b/ecmascript/base/json_helper.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ECMASCRIPT_BASE_JSON_HELPER_H
+#define ECMASCRIPT_BASE_JSON_HELPER_H
+
+#include "ecmascript/js_handle.h"
+#include "ecmascript/mem/c_string.h"
+#include "ecmascript/property_attributes.h"
+
+namespace panda::ecmascript::base {
+
+class JsonHelper {
+public:
+ static CString ValueToQuotedString(CString str);
+
+ static bool IsFastValueToQuotedString(const char *value);
+
+ static inline bool CompareKey(const std::pair, PropertyAttributes> &a,
+ const std::pair, PropertyAttributes> &b)
+ {
+ return a.second.GetDictionaryOrder() < b.second.GetDictionaryOrder();
+ }
+
+ static inline bool CompareNumber(const JSHandle &a, const JSHandle &b)
+ {
+ return a->GetNumber() < b->GetNumber();
+ }
+};
+
+} // namespace panda::ecmascript::base
+
+#endif // ECMASCRIPT_BASE_UTF_JSON_H
\ No newline at end of file
diff --git a/ecmascript/base/json_parser.cpp b/ecmascript/base/json_parser.cpp
index 69dbf3405e6f4a5a46f76f64072f10b80e5a2d5a..6cd134cbfa696edc3e58138cf0c2e15c92727d78 100644
--- a/ecmascript/base/json_parser.cpp
+++ b/ecmascript/base/json_parser.cpp
@@ -22,9 +22,11 @@ JSHandle Internalize::InternalizeJsonProperty(JSThread *thread, c
{
JSHandle
|
|---|
|
|---|